目录
自动批处理:合并多个setState()、只触发一次render()
flushSync(callback):强制同步刷新DOM,挂起其他所有工作
React18前:只在合成事件(onClick)和钩子函数(usestate)有用
React18后:在Promise、setTimeout等原生事件中也有用
比较更新前后的state,所以不能直接修改引用类型,应当新建+...
const [state, setState] = useState(initialState: S | (() => S))
setState(state: (prevState, props) => (return newState),callback?: () => void): void;
推荐用法 ()=>{return },this.state.
const [state, dispatch] = useReducer(reducer, initialArg, init?)
定义:组件的私有属性,异步更新的响应式数据
组件的私有属性,值是对象,当 state 发生变化时会触发 React 组件重新渲染视图
即响应式数据
props 同步更新,state 异步更新的,最好不要用prevState值去计算下一个 state 的值
加入队列,只影响从 下一个 渲染开始返回的 this.state
setState
其实本身执行的过程和代码都是同步的
自动批处理:合并多个setState()、只触发一次render()
同一时机多次调用`setState()`方法的一种处理机制,有助于减少在状态更改时发生的重新渲染次数。
handleClick = () => {
this.setState({
msg: 'hi'
});
this.setState({
count: 1
});
}
//虽然调用了两次`setState()`方法,但是只会触发一次`render()`方法的重新执行。
不合并setState():setState()的回调函数
//同样的数据修改只会修改一次,可利用`setState()`的回调函数写法来保证每一次都能触发。
//因为不能比较函数体是否一致
this.setState((state)=> ({count: state.count + 1}));
this.setState((state)=> ({count: state.count + 1}));
flushSync(callback):强制同步刷新DOM,挂起其他所有工作
let { useState } = React;
let { flushSync } = ReactDOM;
let Welcome = (props) => {
const [count, setCount] = useState(0);
const handleClick = () => {
flushSync(()=>{
setCount(count + 1)
})
}
return (
<div>
<button onClick={handleClick}>点击</button>
<div>hello world, { count }, { msg }</div>
</div>
);
}
React18前:只在合成事件(onClick)和钩子函数(usestate)有用
原生自带的事件监听
addEventListener
,或者也可以用原生js、jq直接document.querySelector().onclick
这种绑定事件的形式都属于原生事件。react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件。
像在
jsx
中常见的onClick
、onChange
这些都是合成事件function App () { const [ count , setCount ] = useState ( 0 ) ; const [ flag , setFlag ] = useState ( false ) ; function handleClick ( ) { setCount ( c => c + 1 ) ; // 还没有重新渲染 setFlag ( f => ! f ) ; // 还没有重新渲染 // React 只会在最后重新渲染一次(这是批处理!) } return ( < div > < button onClick = { handleClick } > Next < / button > < h1 style = { { color : flag ? "blue" : "black" } } > { count } < / h1 > < / div > ) ; }
React18后:在Promise、setTimeout等原生事件中也有用
handleClick = () => {
setTimeout(()=>{
this.setState({
msg: 'hi'
});
this.setState({
count: 1
});
}, 2000)
}
//上面代码在React18之前的版本中,将会触发两次`render()`方法。
//默认是自动批处理的,当然也可以改成不是自动批处理的方式,通过`ReactDOM.flushSync`这个方法。
import { flushSync } from 'react-dom'; // Note: react-dom, not react
handleClick = () => {
ReactDOM.flushSync(()=>{
this.setState({
msg: 'hi'
});
})
ReactDOM.flushSync(()=>{
this.setState({
count: 1
});
})
}
object.is():浅比较
比较更新前后的state,所以不能直接修改引用类型,应当新建+...
function reducer(state, action) {
switch (action.type) {
case 'incremented_age': {
// ✅ 修复:创建一个新的对象
return {
...state,
age: state.age + 1
};
}
case 'changed_name': {
// ✅ 修复:创建一个新的对象
return {
...state,
name: action.nextName
};
}
// ...
}
}
const [state, setState] = useState(initialState: S | (() => S))
setState(state: (prevState, props) => (return newState),callback?: () => void): void;
this.setState:对象与原状态进行浅合并
this.setState({count:1})
//箭头函数:相当于直接使用返回对象 {count: 1}
this.setState(() => ({count: 1}));
浅合并(shallow merge)是指将两个对象进行合并,如果两个对象有相同的属性名,则后一个对象的属性值会覆盖前一个对象的属性值
但是,如果属性值是一个对象,则不会进行递归合并,而是直接用后一个对象的属性值替换前一个对象的属性值。
import FormRender, { useForm } from 'form-render';
const form = useForm();
//formData 为form.getValues()的非空值
setData({ ...data, ...formData });
setState不会合并之前的值:...
const [info, setInfo] = useState({
username: 'xiaoming',
age: 20
})
setInfo({
...info,
username: 'xiaoqiang'
})
推荐用法 ()=>{return },this.state.
setState时使用函数返回新state值,(避免多个 setState
同时调用导致的状态更新冲突问题)
在回调函数中获取最新更新后的state
handleClick = () => {
this.setState(() => { return { board: this.jsonMsg[0].data.board_data[Number(key)] };},
() => {
console.log('Updated count:', this.state.count);
});
};
useReducer:复杂state
const [state, dispatch] = useReducer(reducer, initialArg, init?)
let { useReducer } = React;
let loginState = {//体现整体关联性与统一性**
isLogin: true,
isLogout: false
}
let loginReducer = (state, action) => {
switch(action.type){
case 'login':
return { isLogin: true, isLogout: false }
case 'logout':
return { isLogin: false, isLogout: true }
default:
throw Error('Unknown action: ' + action.type);
}
}
let Welcome = (props) => {
const [ state, loginDispatch ] = useReducer(loginReducer, loginState);
const handleLogin = () => {
loginDispatch({ type: 'login' });
}
const handleLogout = () => {
loginDispatch({ type: 'logout' });
}
return (
<div>
{ state.isLogin ? <button onClick={handleLogout}>退出</button> : <button onClick={handleLogin}>登录</button> }
</div>
);
}
dispatch action
手动调用reducer
const action = { type: 'incremented_age' };
dispatch(action);
const nextState = reducer(state, action);
console.log(state); // { age: 42 }
console.log(nextState); // { age: 43 }
受控组件和非受控组件
受控和非控:对某个组件状态的掌控,它的值是否只能由用户设置,而不能通过代码控制。
非受控组件:DOM操作
现用现取,官方建议尽量少用ref,用多了有一定的效率影响
handleSubmit = (event) => {
event.preventDefault() //阻止表单提交
const {username, password} = this//拿到的是form下的username, password结点
alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>登录</button>
受控组件:state维护
class Login extends React.Component {
//state最好要初始化状态
state = {
username: '', //用户名
password: '' //密码
}
//保存用户名到状态中
saveUsername = (event) => {
this.setState({username: event.target.value})
}
//保存密码到状态中
savePassword = (event) => {
this.setState({password: event.target.value})
}
//表单提交的回调
handleSubmit = (event) => {
event.preventDefault() //阻止表单提交
const {username, password} = this.state//获得的是state下的username,password值
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
执行过程:当前组件state队列、react批量更新状态
当所有组件didmount
后,父组件didmount
,会将isBranchUpdate
设置为false。这时将执行之前累积的setState,
更新时会把每个组件的更新合并,每个组件只会触发一次更新的生命周期。
_pendingStateQueue
:当前组件等待执行更新的state
队列isBatchingUpdates
:react用于标识当前是否处于批量更新状态,所有组件公用dirtyComponent
:当前所有处于待更新状态的组件队列
事务机制:数据库特有的术语,同步发生数据更新时,防止数据的不一致。
1.将setState传入的partialState
参数存储在当前组件实例的state暂存队列中。
2.判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。
3.如果未处于批量更新状态,将批量更新状态标识设置为true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。
4.调用事务的waper
方法,遍历待更新组件队列依次执行更新。
5.执行生命周期componentWillReceiveProps
。
6.将组件的state暂存队列中的state
进行合并,获得最终要更新的state对象,并将队列置为空。
7.执行生命周期componentShouldUpdate
,根据返回值判断是否要继续更新。
8.执行生命周期componentWillUpdate
。
9.执行真正的更新render
。
10.执行生命周期component
DidUpdate
。