useState
const [state, setState] = useState(initialState)
返回一个 state,以及更新 state 的函数。
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用:
const [state, setState] = useState(() => {
const initialState = []
for (let i = 0; i < 100; i++) {
initialState.push(i)
}
return initialState
})
如果你更新 State Hook 后的 state 与当前的 state 相同时,React 将跳过子组件的渲染并且不会触发 effect 的执行。(React 使用 Object.is 比较算法 来比较 state。)
function Child () {
useEffect(() => {
console.log('Child')
})
return <div>Child</div>
}
function App() {
const [val, setVal] = useState(1)
const handleClick = (e) => {
setVal(1)
}
return (
<div className="App">
<div>{val}</div>
<button onClick={handleClick}>点击</button>
<Child></Child>
</div>
);
}
某些场景下,useReducer
会比 useState
更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer
还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
function Child (props) {
console.log('重新渲染')
function handleClick () {
props.dispatch({type: 'increment'})
}
return (
<button onClick={handleClick}>按钮</button>
)
}
Child = React.memo(Child)
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
function handleClick() {
dispatch({type: 'increment'})
}
return (
<div>
<h1 onClick={handleClick}>{state.count}</h1>
<Child dispatch={dispatch} />
</div>
)
}
React 会确保 dispatch 函数的标识是稳定的,并且不会在组件重新渲染时改变。这就是为什么可以安全地从 useEffect 或 useCallback 的依赖列表中省略 dispatch。
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
function handleClick() {
dispatch({type: 'increment'})
}
const callback = useCallback(() => {
dispatch({type: 'increment'})
}, [])
return (
<div>
<h1 onClick={handleClick}>{state.count}</h1>
<Child callback={callback} />
</div>
)
}
如果直接从useReducer返回操作,则其行为与useState几乎相同。
function App() {
const [name, setName] = useReducer((_, value) => value, '请输入');
return (
<div className="App">
<input value={name} onChange={e => setName(e.target.value)} />
</div>
);
}