React的组件就是一个js函数,函数内部return一个由jsx语法创建的html代码片段。
//MyComp.js
export default function MyComp(){
return (
<h1>我是新组件MyComp</h1>
)
}
在需要引入组件的地方import导入组件,并放在相应位置
//App.js
import MyComp from './components/MyComp'
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
编辑 <code>src/App.js</code> 并且重新加载
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
//新组件
<MyComp></MyComp>
</header>
</div>
);
}
export default App;
需要注意的地方
js中的变量可以通过"{}"的方式传入JSX中,从而使html中渲染的数据可以得到控制。任何 JavaScript 表达式都可以在大括号之间工作,包括像 formatDate() 这样的函数调用:
export default function TodoList() {
const name = 'Gregorio Y. Zara';
return (
<h1>{name}'s To Do List</h1>
);
}
//函数调用
const today = new Date();
function formatDate(date) {
return new Intl.DateTimeFormat(
'en-US',
{ weekday: 'long' }
).format(date);
}
export default function TodoList() {
return (
<h1>To Do List for {formatDate(today)}</h1>
);
}
{name}'s To Do List\.
有效,但<{tag}>Gregorio Y. Zara's To Do List{tag}>
无效。除了字符串、数字和其他js表达式外,甚至可以在jsx中插入对象。在jsx中插入对象,必须将对象封装在另一个对大括号中。person={{name:'henry',age:22}}
一般在插入内联样式的时候会用到双大括号
export default function TodoList() {
return (
<ul style={{
backgroundColor: 'black',
color: 'pink'
}}>
<li>Improve the videophone</li>
<li>Prepare aeronautics lectures</li>
<li>Work on the alcohol-fuelled engine</li>
</ul>
);
React通过传递属性(props)相互通信。每个父组件都可以通过给他们属性将一些信息传递给他们的子组件
首先在父组件中给子组件添加属性
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
function Avatar({ person, size }) {
// person and size are available here
}
可以在接受时为属性添加默认值
function Avatar({ person, size = 100 }) {
// ...
}
当组件需要把自身接受到的属性转发给子组件时,可用展开语法精简代码
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
简化后:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
可以把组件作为prop参数传递,父组件通过{children}来接收,类似于vue的slot
import Avatar from './Avatar.js';
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}
通过变量来控制返回的jsx
if (isPacked) {
return <li className="item">{name} ✔</li>;
}
return <li className="item">{name}</li>;
还可以用更简洁的三元运算符进行条件渲染
return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);
当你想在条件为真时渲染一些jsx,或者什么也不渲染,可以使用“&&”
return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);
可以通过js数组的filter() 和 map() 来过滤数据数组并将其转换为组件数组。
const people = [
'Creola Katherine Johnson: mathematician',
'Mario José Molina-Pasquel Henríquez: chemist',
'Mohammad Abdus Salam: physicist',
'Percy Lavon Julian: chemist',
'Subrahmanyan Chandrasekhar: astrophysicist'
];
export default function List() {
const listItems = people.map(person =>
<li>{person}</li>
);
return <ul>{listItems}</ul>;
}
const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];
export default function List() {
const chemists = people.filter(person =>
person.profession === 'chemist'
);
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
return <ul>{listItems}</ul>;
}
在组件内定义事件响应函数,并把函数作为属性值传递到JSX上
export default function Button() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
也可以直接在JSX中内联事件
<button onClick={function handleClick() {
alert('You clicked me!');
}}>
组件内需要存储的数据叫做状态。状态相当于组件的内存,存放着需要记忆的数据。
import { useState } from 'react';
创建state使用useState()来创建,他需要给定一个初始值作为参数。创建一个数组来接收这个状态,这个数组包含一个state的名称,和一个修改这个state的方法。
const [isSent, setIsSent] = useState(false);
const [message, setMessage] = useState('Hi!');
state的值不能直接修改,需要借用useState提供的第二个参数。
setIsSent(true);
setXXX()方法会触发React进行重新渲染。
如果state的值是数组或者对象,那么使用setXXX()方法修改时就不能直接修改原值,而是需要重新创建一份赋给state
修改对象一般通过扩展运算符、Object.assign()等方法来复制原对象然后重新赋值给state
export default function Form() {
const [person, setPerson] = useState({
firstName: 'Barbara',
lastName: 'Hepworth',
email: '[email protected]'
});
function handleFirstNameChange(e) {
setPerson({
//通过扩展运算符复制一份原对象给新对象再赋值给state
...person,
firstName: e.target.value
});
}
修改数组一般通过map()、filter()、slice()等方法复制一份新数组再赋值给state
import { useState } from 'react';
let nextId = 0;
export default function List() {
const [name, setName] = useState('');
const [artists, setArtists] = useState([]);
return (
<>
<h1>Inspiring sculptors:</h1>
<input
value={name}
onChange={e => setName(e.target.value)}
/>
<button onClick={() => {
setArtists([
...artists,
{ id: nextId++, name: name }
]);
}}>Add</button>
<ul>
{artists.map(artist => (
<li key={artist.id}>{artist.name}</li>
))}
</ul>
</>
);
}
如果对象或数组有深层嵌套用普通的复制方法比较复杂,使用immer插件可以简化操作。它可以让你像修改原数组那样直接修改对象的属性或数组中的某项值,但其实他在内部已经帮你创建了一个副本并赋予state
步骤
使用方法
import { useImmer } from 'use-immer';
export default function Form() {
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
function handleTitleChange(e) {
updatePerson(draft => {
draft.artwork.title = e.target.value;
});
}
父传子通过props进行,子传父通过调用父组件定义的方法来实现,此方法通过prop传递给子组件
父传子
//子组件
function Child (prop){
return
<div>{props.msg}</div>
}
//父组件
function Father (){
let msg = 'hello world'
return (
<Child msg={msg}></Child>
)
}
子传父
// 子传父,通过 props 调用父组件的方法
this.props.handleClick(xxx)
// 父组件中
<Child handleClick={this.handleClick}></Child>
兄弟传值
可以通过context来进行兄弟组件传值。context不仅可以用于兄弟组件传值,还可以用于更复杂关系的组件的传值。
引入
export const { Provider, Consumer } = React.createContext("sky")
在兄弟组件的共同上层定义一个Provider,通过provider中的value属性把想要传递的值赋予它
export default class ClassComp extends React.Component {
constructor(props) {
super(props)
this.state = {
name: 'tracer'
}
}
render() {
return (
<Provider value={this.state.name}>
<div>
<h1>爷爷:{this.state.name}</h1>
<ClassComp2 />
</div>
</Provider>
)
}
}
这个Provider所在的组件的所有下级都将共享这个数据。后代组件通过在该组件中创建的Consumer来获取值。
import { Consumer } from './ClassComp' //引入
export default class ClassComp2 extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
render() {
return(
<Consumer>
{(value)=>
<div>
<h1>爸爸:{value}</h1>
<ClassComp3/>
</div>
}
</Consumer>
)
}
}