Posted on:
Last modified:
useState
可以用来管理一个组件比较简单的一两个状态,如果状态多了不适合使用 useState 管理,
可以使用 useReducer.
import React, {useState} from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(c => c + 1)}> Click me </button>
</div>
);
}
export default Counter;
setState 在更新函数中可能表现为异步,保险起见,应该使用一个函数作为它的参数,而不是直接 使用值作为参数。比如在上面的例子中:
// 正确
<button onClick={() => setCount(c => c + 1)}> Click me </button>
// 错误
<button onClick={() => setCount(c + 1)}> Click me </button>
还有一个比较常见的模式是使用一个 object 保存多个状态,在部分更新 state 的时候,可以这样做:
const [state, setState] = useState({
"showText": true,
"showShadow": true
})
// 使用 {...s, foo: bar} 避免重复代码
<button onClick={() => setState(s => {...s, showText: false})}>Hide Text</button>
useRef
钩子和 useState
比较类似,都用于在不同的重绘之间保存状态。区别也很明显:
useState 中的 setState 会导致重绘,而改变 useRef 的 .current
则不会。
useEffect hook 用来实现一些副作用,一般可以用作页面首次加载时读取数据。
import React, {useState, useEffect} from 'react';
function App() {
const [isOn, setIsOn] = useState(false);
useEffect(() => {
let interval;
if (isOn) {
interval = setInterval(() => console.log('tick'), 1000);
}
return () => clearInterval(interval);
}, [isOn]);
...
}
export default App;
在 useEffect 中返回的函数会被用来做垃圾清理。另外,组件初始化的时候总会触发一次 useEffect.
默认情况下,每次 state 有改变的时候,都会调用 useEffect 函数。如果需要更改触发的时机,
那么需要使用 useEffect 的第二个参数来指定监听的事件或者说状态。当第二个参数只使用一个空
数组 []
的时候就只会在组件加载的时候调用。数组中有哪些变量,表示在这些变量变化的时候调用。
一般建议把不依赖 props 和 state 的函数提到组件外,并把仅被 effect 使用的函数放到 effect 里。
useReducer 和 useState 的用途基本一样,但是当状态比较复杂的时候,最好使用 useReducer. 有了 useReducer,基本可以不用 redux 了。
const [state, dispatch] = useReducer(reducer, initialState [, init]);
useReducer 通常放在一组状态的根元素层级,比如一个页面。dispatch 函数触发事件,reducer 函数用来处理事件,更新 state.
注意,useReducer 的 reducer 函数,和 redux 不同,不需要 state=initialState
参数。
默认参数在调用 useReducer 的时候已经给出了。
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {...state, count: state.count + 1};
case 'decrement':
return {...state, count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
Context 用来向所有后代元素广播状态,可以跳跃组件树层级,而不需要层层传递。
React.createContext
定义一个高层次 Context,<MyContext.Provider />
来包裹需要接受这个 context 的所有组件。<MyContext.Consumer />
, 访问 Context 中的值const AppContext = createContext()
function App({children}) {
return <AppContext.Provider value={42}>
{children}
</AppContext.Provider>
}
function Page() {
return <AppContext.Consumer>
<span>{value}</span>
</AppContext.Consumer>
}
除了使用使用 <Consumer/>
以外,我们还可以使用 useContext 钩子来读取 Context 中的值。
这样就不用额外再嵌套一层 Consumer 了。
function Page() {
// 注意这里的参数是 Context,而不是 Consumer
const value = useContext(AppContext);
return <span>{value}</span>;
}
可以组合使用 useReducer 和 useContext 来实现 Redux 的功能。只需要用 Context.Provider 把
state 和 dispatch 这两个变量广播给 root 下的所有元素,这样在需要使用 state 和 dispatch 的
地方直接 useContext(Context)
就好了。
用来保证子组件 props 的 Referential equality,实际很少用到。
useCallback(fn, []) // is equal to
useMemo(() => fn, [])
网上有些博客中说到的「useCallback 改善性能」大多属于误用。
通过灵活组合 useState, 和 useEffect, 我们完全可以创建自己的钩子。
import React from 'react';
function useOffline() {
const [isOffline, setIsOffline] = React.useState(false);
function onOffline() {
setIsOffline(true);
}
function onOnline() {
setIsOffline(false);
}
React.useEffect(() => {
window.addEventListener('offline', onOffline);
window.addEventListener('online', onOnline);
return () => {
window.removeEventListener('offline', onOffline);
window.removeEventListener('online', onOnline);
};
}, []);
return isOffline;
}
function App() {
const isOffline = useOffline();
if (isOffline) {
return <div>Sorry, you are offline ...</div>;
}
return <div>You are online!</div>;
}
export default App;
© 2016-2022 Yifei Kong. Powered by ynotes
All contents are under the CC-BY-NC-SA license, if not otherwise specified.
Opinions expressed here are solely my own and do not express the views or opinions of my employer.