useState

useState 可以让你的函数组件也具备类组件的 state 功能

使用语法如下:
const [state, setState] = useState(initialState);
useState 返回一个数组,第一个是 state 的值,第二个是更新 state 的函数

在真实的程序中我们可以这样使用:

  1. function TestUseState() {
  2. const [count, setCount] = React.useState(0);
  3. return (
  4. <div>
  5. <p>useState api</p>
  6. <p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
  7. </div>
  8. )
  9. }

使用 useState 需要注意一个事项,当你初始化是一个对象时,使用 setCount 不像类组件的 this.setState 会自动合并到 state 中。setCount 会使用当前的值覆盖之前的 state。如下所示:

  1. function TestUseStateObject() {
  2. const [state, setState] = React.useState({
  3. count: 0,
  4. greeting: "Hello, World!",
  5. });
  6. const handleAdd = () => {
  7. setState({
  8. count: state.count + 1
  9. })
  10. }
  11. console.log('state > ', state)
  12. return (
  13. <div>
  14. <p>useStateObject api</p>
  15. <p>Count: {state.count} <button onClick={handleAdd}>自增</button></p>
  16. </div>
  17. )
  18. }

React Hooks 常用 api - 图1

我们可以看到,当点击按钮时 state 被替换成了 {count: 1}。如果想要在 state 中使用一个对象需要在更新值的时候把之前的值解构出来,如下所示:

  1. setState({
  2. ...state,
  3. count: state.count + 1
  4. })

useEffect

默认情况下 useEffect 在完成渲染后运行,我们可以在这里获取 DOM 和处理其他副作用。但它还有两种不同的运行阶段

  1. function TestUseEffect() {
  2. const [count, setCount] = React.useState(0);
  3. React.useEffect(() => {
  4. console.log(`组件被更新,Count: ${count}`);
  5. });
  6. return (
  7. <div>
  8. <p>useEffect api</p>
  9. <p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
  10. </div>
  11. )
  12. }

上面的 useEffect 在每次组件渲染后运行,每当我们点击自增按钮都会执行一次

但是如果上面的代码在每次渲染后都执行,如果我们在 useEffect 从服务器拉取数,造成的结果就是每次渲染后都会从服务器拉取数,或者是只有某些 props 被更新后才执行 useEffect。那么默认的 useEffect 就不是我们想要的执行方式,这时 useEffect 提供了第二个参数

useEffect(disUpdate, [])
useEffect 第二个参数为一个数组。当我们提供第二个参数时,只有第二个参数被更改 useEffect 才会执行。利用第二个参数我们可以模拟出类组件的 componentDidMount 生命周期函数

  1. function TestUseEffectListener() {
  2. const [count, setCount] = React.useState(0);
  3. React.useEffect(() => {
  4. console.log('componentDidMount fetch Data...');
  5. }, []);
  6. return (
  7. <div>
  8. <p>TestUseEffectListener</p>
  9. <p>Count: {count} <button onClick={() => setCount(count + 1) }>自增</button></p>
  10. </div>
  11. )
  12. }

上面的代码中 useEffect 只会执行一次,当你点击自增 useEffect 也不会再次执行

在 useEffect 第一个参数的函数中我们可以返回一个函数用于执行清理功能,它会在 UI 组件被清理之前执行,结合上面所学的知识使用 useEffect 模拟 componentWillUnMount 生命周期函数

  1. function TestUseEffectUnMount() {
  2. const [count, setCount] = React.useState(0);
  3. React.useEffect(() => {
  4. return () => {
  5. console.log('componentUnmount cleanup...');
  6. }
  7. }, []);
  8. return (
  9. <div>
  10. <p>TestUseEffectUnMount</p>
  11. </div>
  12. )
  13. }

上面的代码中,当组件 TestUseEffectUnMount 将要销毁时,会执行 console.log() 代码

useContext

useContext 可以让你在函数中使用 context,它有效的解决了以前 Provider、Consumer 需要额外包装组件的问题

使用语法如下:
const context = useContext(Context);

现在让我们来看看实际应用中这个 useContext 是如何使用的,代码如下:

  1. function TestFuncContext() {
  2. const context = React.useContext(ThemeContext);
  3. return (
  4. <div style={context}>TestFuncContext</div>
  5. )
  6. }

我们可以看到上面直接使用 React.useContext(ThemeContext) 就可以获得 context,而在之前的版本中需要像这样才能获取 ({value} => {}),这极大的简化了代码的书写

  1. // 之前Consumer的访问方式
  2. function TestNativeContext() {
  3. return (
  4. <ThemeContext.Consumer>
  5. {(value) => {
  6. return (
  7. <div style={value}>TestNativeContext</div>
  8. )
  9. }}
  10. </ThemeContext.Consumer>
  11. );
  12. }

useReducer

useReducer 是 useState 的替代方案,当你有一些更复杂的数据时可以使用它

使用语法如下:
const [state, dispatch] = useReducer(reducer, initialState);
第一个参数是一个 reducer 用来处理传过来的 action,函数申明为:(state, action) => ();第二个参数是一个初始化的 state 常量

在返回值 [state, dispatch] 中,state 就是你的数据,dispatch 可以发起一个 action 到 reducer 中处理。这个功能感觉就是组件本地的redux,在设计一些复杂的数据结构时可以使用

现在我们看看实际应用中这个 useReducer 是如何使用的,代码如下:

  1. function TestUseReducer() {
  2. const [state, setState] = React.useReducer((state, action) => {
  3. switch(action.type) {
  4. case 'update':
  5. return {name: action.payload}
  6. default:
  7. return state;
  8. }
  9. }, {name: ''});
  10. const handleNameChange = (e) => {
  11. setState({type: 'update', payload: e.target.value})
  12. }
  13. return (
  14. <div>
  15. <p>你好:{state.name}</p>
  16. <input onChange={handleNameChange} />
  17. </div>
  18. )
  19. }

当改变 input 中的值时会同时更新 state 中的数据,然后显示在界面上

useCallback

useCallback 和 useMemo 有些相似,它接收一个内联函数和一个数组,它返回的是一个记忆化版本的函数

使用语法如下:
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
useCallback 的第一个参数是一个函数,用来执行一些操作和计算;第二个参数是一个数组,当这个数组里面的值改变时 useCallback 会重新执行更新这个匿名函数里面引用到的值。这样描述可能有点不太好理解,下面看一个例子:

  1. function TestUseCallback({ num }) {
  2. const memoizedCallback = React.useCallback(
  3. () => {
  4. // 一些计算
  5. return num;
  6. },
  7. [],
  8. );
  9. console.log('记忆 num > ', memoizedCallback())
  10. console.log('原始 num > ', num);
  11. return (
  12. <div>
  13. <p>TestUseCallback</p>
  14. </div>
  15. )
  16. }

React Hooks 常用 api - 图2

如果我们想监听 num 值的更新重新做一些操作和计算,我们可以给第二个参数放入 num 值,像下面这样:

  1. function TestUseCallback({ num }) {
  2. const memoizedCallback = React.useCallback(
  3. () => {
  4. // 一些计算
  5. return num;
  6. },
  7. [num],
  8. );
  9. console.log('记忆 num > ', memoizedCallback())
  10. console.log('原始 num > ', num);
  11. return (
  12. <div>
  13. <p>TestUseCallback</p>
  14. </div>
  15. )
  16. }

useRef

useRef 的功能有点像类属性,或者说你想要在组件中记录一些值,并且这些值在稍后可以更改

使用语法如下:
const refContainer = useRef(initialValue);
useRef 返回一个可变的对象,对象的 current 属性被初始化为传递的参数(initialValue),返回的对象将持续整个组件的生命周期

一个保存 input 元素,并使其获取焦点程序,代码如下:

  1. function TestUseRef() {
  2. const inputEl = React.useRef(null);
  3. const onButtonClick = () => {
  4. // 点击按钮会设置input获取焦点
  5. inputEl.current.focus(); // 设置useRef返回对象的值
  6. };
  7. return (
  8. <div>
  9. <p>TestUseRef</p>
  10. <div>
  11. <input ref={inputEl} type="text" />
  12. <button onClick={onButtonClick}>input聚焦</button>
  13. </div>
  14. </div>
  15. )
  16. }

useRef 返回的对象可以在其他地方设置比如:useEffect、useCallback 等