useState
useState 可以让你的函数组件也具备类组件的 state 功能
使用语法如下:
const [state, setState] = useState(initialState);
useState 返回一个数组,第一个是 state 的值,第二个是更新 state 的函数
在真实的程序中我们可以这样使用:
function TestUseState() {const [count, setCount] = React.useState(0);return (<div><p>useState api</p><p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p></div>)}
使用 useState 需要注意一个事项,当你初始化是一个对象时,使用 setCount 不像类组件的 this.setState 会自动合并到 state 中。setCount 会使用当前的值覆盖之前的 state。如下所示:
function TestUseStateObject() {const [state, setState] = React.useState({count: 0,greeting: "Hello, World!",});const handleAdd = () => {setState({count: state.count + 1})}console.log('state > ', state)return (<div><p>useStateObject api</p><p>Count: {state.count} <button onClick={handleAdd}>自增</button></p></div>)}

我们可以看到,当点击按钮时 state 被替换成了 {count: 1}。如果想要在 state 中使用一个对象需要在更新值的时候把之前的值解构出来,如下所示:
setState({...state,count: state.count + 1})
useEffect
默认情况下 useEffect 在完成渲染后运行,我们可以在这里获取 DOM 和处理其他副作用。但它还有两种不同的运行阶段
function TestUseEffect() {const [count, setCount] = React.useState(0);React.useEffect(() => {console.log(`组件被更新,Count: ${count}`);});return (<div><p>useEffect api</p><p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p></div>)}
上面的 useEffect 在每次组件渲染后运行,每当我们点击自增按钮都会执行一次
但是如果上面的代码在每次渲染后都执行,如果我们在 useEffect 从服务器拉取数,造成的结果就是每次渲染后都会从服务器拉取数,或者是只有某些 props 被更新后才执行 useEffect。那么默认的 useEffect 就不是我们想要的执行方式,这时 useEffect 提供了第二个参数
useEffect(disUpdate, [])
useEffect 第二个参数为一个数组。当我们提供第二个参数时,只有第二个参数被更改 useEffect 才会执行。利用第二个参数我们可以模拟出类组件的 componentDidMount 生命周期函数
function TestUseEffectListener() {const [count, setCount] = React.useState(0);React.useEffect(() => {console.log('componentDidMount fetch Data...');}, []);return (<div><p>TestUseEffectListener</p><p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p></div>)}
上面的代码中 useEffect 只会执行一次,当你点击自增 useEffect 也不会再次执行
在 useEffect 第一个参数的函数中我们可以返回一个函数用于执行清理功能,它会在 UI 组件被清理之前执行,结合上面所学的知识使用 useEffect 模拟 componentWillUnMount 生命周期函数
function TestUseEffectUnMount() {const [count, setCount] = React.useState(0);React.useEffect(() => {return () => {console.log('componentUnmount cleanup...');}}, []);return (<div><p>TestUseEffectUnMount</p></div>)}
上面的代码中,当组件 TestUseEffectUnMount 将要销毁时,会执行 console.log() 代码
useContext
useContext 可以让你在函数中使用 context,它有效的解决了以前 Provider、Consumer 需要额外包装组件的问题
使用语法如下:
const context = useContext(Context);
现在让我们来看看实际应用中这个 useContext 是如何使用的,代码如下:
function TestFuncContext() {const context = React.useContext(ThemeContext);return (<div style={context}>TestFuncContext</div>)}
我们可以看到上面直接使用 React.useContext(ThemeContext) 就可以获得 context,而在之前的版本中需要像这样才能获取
// 之前Consumer的访问方式function TestNativeContext() {return (<ThemeContext.Consumer>{(value) => {return (<div style={value}>TestNativeContext</div>)}}</ThemeContext.Consumer>);}
useReducer
useReducer 是 useState 的替代方案,当你有一些更复杂的数据时可以使用它
使用语法如下:
const [state, dispatch] = useReducer(reducer, initialState);
第一个参数是一个 reducer 用来处理传过来的 action,函数申明为:(state, action) => ();第二个参数是一个初始化的 state 常量
在返回值 [state, dispatch] 中,state 就是你的数据,dispatch 可以发起一个 action 到 reducer 中处理。这个功能感觉就是组件本地的redux,在设计一些复杂的数据结构时可以使用
现在我们看看实际应用中这个 useReducer 是如何使用的,代码如下:
function TestUseReducer() {const [state, setState] = React.useReducer((state, action) => {switch(action.type) {case 'update':return {name: action.payload}default:return state;}}, {name: ''});const handleNameChange = (e) => {setState({type: 'update', payload: e.target.value})}return (<div><p>你好:{state.name}</p><input onChange={handleNameChange} /></div>)}
当改变 input 中的值时会同时更新 state 中的数据,然后显示在界面上
useCallback
useCallback 和 useMemo 有些相似,它接收一个内联函数和一个数组,它返回的是一个记忆化版本的函数
使用语法如下:
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useCallback 的第一个参数是一个函数,用来执行一些操作和计算;第二个参数是一个数组,当这个数组里面的值改变时 useCallback 会重新执行更新这个匿名函数里面引用到的值。这样描述可能有点不太好理解,下面看一个例子:
function TestUseCallback({ num }) {const memoizedCallback = React.useCallback(() => {// 一些计算return num;},[],);console.log('记忆 num > ', memoizedCallback())console.log('原始 num > ', num);return (<div><p>TestUseCallback</p></div>)}

如果我们想监听 num 值的更新重新做一些操作和计算,我们可以给第二个参数放入 num 值,像下面这样:
function TestUseCallback({ num }) {const memoizedCallback = React.useCallback(() => {// 一些计算return num;},[num],);console.log('记忆 num > ', memoizedCallback())console.log('原始 num > ', num);return (<div><p>TestUseCallback</p></div>)}
useRef
useRef 的功能有点像类属性,或者说你想要在组件中记录一些值,并且这些值在稍后可以更改
使用语法如下:
const refContainer = useRef(initialValue);
useRef 返回一个可变的对象,对象的 current 属性被初始化为传递的参数(initialValue),返回的对象将持续整个组件的生命周期
一个保存 input 元素,并使其获取焦点程序,代码如下:
function TestUseRef() {const inputEl = React.useRef(null);const onButtonClick = () => {// 点击按钮会设置input获取焦点inputEl.current.focus(); // 设置useRef返回对象的值};return (<div><p>TestUseRef</p><div><input ref={inputEl} type="text" /><button onClick={onButtonClick}>input聚焦</button></div></div>)}
useRef 返回的对象可以在其他地方设置比如:useEffect、useCallback 等
