React元素
使用 JSX 声明一个元素:
const element = <h1>Hello, World!</h1>
JSX 与函数调用的等效性:Babel 会把 JSX 转译成一个名为 React.createElement()
函数调用。
一下两种表示是等效的:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
等效于
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
在背后,React.createElement()
创建了这样的对象(简化后):
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
这些对象被称为 ” React 元素 “ 。React 使用它们构建DOM。
元素渲染
假设HTML文件中有一个根节点:
<div id="root"></div>
将React元素渲染到根节点中:
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
组件
组件接收一些入参(存储在props中),并返回描述页面展示内容的 React 元素。
组件有两种:函数组件和class组件。
定义一个函数组件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
定义一个class组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
渲染组件
React 元素可以是DOM标签或用户自定义的组件:
const element = <div />;
const element = <Welcome name="Sara" />;
将组件渲染到页面上:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
组合组件
组件可以在其输出中引用其他组件。
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
props 的只读性
所有React组件都不能修改自身的props。
class 组件
React组件除了函数组件还有class组件,最基本的class组件包括构造函数constructor和render()方法。构造函数中必须首先使用props调用父类构造函数super(props)
,然后可以添加该组件的 statethis.state={}
。
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
class组件的生命周期方法
当组件第一次被渲染到DOM中时,称为挂载mount;DOM中组件被删除时,称为卸载unmount。
可在class组件中声明一些特殊的生命周期方法,在挂载或卸载时执行。
class Clock extends React.Component {
constructor(props) {}
componentDidMount() {}// 挂载
componentWillUnmount() {}// 卸载
render() {}
}
使用this.setState()
设置组件的state,例如如下的时钟组件:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
关于state
不要直接修改state,而应该使用setState()
:
this.state.comment = 'Hello';// 不会重新渲染组件
this.setState({comment: 'Hello'});// OK
构造函数是唯一可以给this.state
赋值的地方。
state的更新可能是异步的,因为this.props
和this.state
可能会异步更新,所以不要依赖它们更新下一个状态。
例如下面可能无法更新counter
this.setState({
counter: this.state.counter + this.props.increment,
});
可以让setState()接收一个函数而不是对象,该函数接收上一个state作为第一个参数,props作为第二个参数:
this.setState((state, props) => ({
counter: state.counter + props.increment
}))
// 使用普通函数
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
state的更新会被合并,多次setState
的单独更新会合并。
state是局部的,可作为 props
向下传递。
<FormattedDate date={this.state.date} />
事件处理
函数组件绑定事件:
function A() {
function handleClick(e) {
e.preventDefault();
}
return (
<a href="" onClick={handleClick}>Click</a>
)
}
class组件绑定事件的几种方式:
一、在构造函数中绑定this
//为了在回调中使用this,需要绑定this
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
}
//在回调中使用this
render() {
return (
<button onClick={this.handleClick}>click</button>
)
}
二、使用箭头函数作为事件回调函数
handleClick = () => {
}
render() {
return (
<button onClick={this.handleClick}>Click</button>
)
}
三、在回调中使用箭头函数
handleClick() {}
render() {
return (
<button onClick={() => this.handleClick()}>Click</button>
)
}
事件传参
<button onClick={(e) => this.deleteRow(id, e)}>Delete</button>
需传入的参数放在前,事件对象e可显式地作为第二个参数传递。
条件渲染
使用元素变量进行条件渲染,根据不同条件将不同元素赋值给变量。
let button;
if(...) {
button = <LogoutButton />
}
else {
button = <LoginButton />
}
与运算符&&,a && expression,a为true时返回 expression,为false时返回false。
三目运算符:
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton />
: <LoginButton />
}
</div>
)
}
在渲染函数中返回null表明该组件不会渲染。
列表渲染
在JSX中使用 {} 插入元素集合渲染一个列表,并给每一项添加一个key。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
在JSX中嵌入map()
上面使用变量存储元素集合后再在 JSX 中包含该变量,也可以直接嵌入map()
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
表单
受控组件:使React组件的state成为表单输入元素的唯一数据源,且React组件能控制表单输入元素随用户输入发生的行为,这样的表单输入元素称为受控组件。
在受控组件上指定value的值会阻止用户更改输入,如果指定value值为 null 或 undefined,则用户仍可编辑输入。
//输入已被锁定
<input value="hi" />
//输入可编辑
<input value={null} />
状态提升
当多个组件需要反应相同的数据变化时,将共享状态提升至最近的共同父组件中,即存储在父组件的state中。
这样,父组件可以通过子组件的 props 的方式为子组件提供数据源及修改数据的行为。
class Parent extends React.Component {
constructor(props) {
super(props);
this.handlerA = this.handlerA.bind(this);
...
this.state = {
prop: 'value'
}
}
handlerA() {
this.setState({prop: 'newValue'})
}
handlerB() {}
render() {
const prop = this.state.prop;
return (
<div>
<ChildA
prop={prop}
eventHandler={this.handlerA}/>
<ChildB
prop="" />
</div>
)
}
}