状态
UI无非就是应用程序的状态的一种表现形式.
我们已经了解ReactDOM.render()
函数可以挂载或局部更新DOM, 但是我们需要一种更精妙的方式.
第一种方式: 渲染并重绘整个根节点
每当应用程序状态有任何更新, 我们就刷新一次根节点, 这样是可以的, 毕竟React只会更新变化的节点, 但是整个树节点都进行一次比对是冗余的.
我们来看这种不推荐的方式:
import ReactDOM from 'react-dom';
import { StrictMode, createElement } from 'react';
document.body.innerHTML = `<div id="app"></div>`;
let count = 0;
function MyTitle() {
return createElement('div', {
style: {
fontSize: '38px',
color: '#880088',
background: 'pink',
},
}, `我觉得不行 +${count}`, ' => ', createElement('button', {
style: {
fontSize: '38px',
},
onClick() {
count++;
render(); // 应用程序状态发生变化, 刷新根节点
},
}, '+1'));
}
function render() {
ReactDOM.render(createElement(StrictMode, null, MyTitle()), app);
}
render(); // 立即挂载根节点
有没有其它方式
根据官网描述, 我们可以只调用一次ReactDOM.render挂载一个根节点, 那怎么更新UI呢?
官网还有以下一些论述:
React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。一个元素就像电影的单帧:它代表了某个特定时刻的 UI。
根据我们已有的知识,更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()。
React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
更新 UI 唯一的方式是创建一个全新的元素
React.Component
类定义了类组件的架构, 其中render()
函数负责创建一个新的React元素, 只要我们遵循以下规则, 就可实现局部更新:
- 通过
setState()
函数更新组件状态, React会调用render()
函数创建新的元素 - 必要时可调用
forceUpdate()
函数强制同步UI
类组件
/**
* 类组件及其状态与UI的更新与同步
*/
import ReactDOM from 'react-dom';
import { Component, createElement } from 'react';
document.body.innerHTML = `<div id="app"></div>`;
class MyTitle extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
console.log('render被调用');
return createElement('div', {
style: {
fontSize: '38px',
color: '#880088',
background: 'pink',
},
}, `我觉得不行 +${this.state.count}`, ' => ',
createElement('button', {
style: {
fontSize: '38px',
},
onClick: () => { // 事件被绑定到DOM上时, this会丢失, 因此必须使用箭头函数.
this.setState({ count: this.state.count + 1 });
// 等价于:
// ++this.state.count && this.forceUpdate();
},
}, '+1'),
);
}
}
function render() {
ReactDOM.render(createElement(MyTitle), app);
}
render(); // 立即挂载根节点
关于setState()
函数
setState()
可能是异步的
出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数。