Skip to content

React Hooks

1. 简介Hooks

Hooks 在2018年10月推出,目的是解决React的状态共享以及组件生命周期管理混乱的问题。

以 use 开头的函数被称为 Hook。useState 是 React 提供的一个内置 Hook。 你可以在 React API 参考 中找到其他内置的 Hook。你也可以通过组合现有的 Hook 来编写属于你自己的 Hook。

React 内置 Hook 文档

2. useState 状态

useState函数是React自带的一个Hook函数,而Hook函数拥有React状态和生命周期管理的能力。 可以看到,useState函数的入参只有一个,就是state的初始值,这个初始值可以是数字、字符串、对象,甚至是一个函数,如下所示。

jsx
function Example (props) {
  const [ count, setCount ] = useState(() => {
    return props.count || 0
  })
  return (
    <div>
      You clicked : { count }
      <button onClick={() => { setCount(count + 1)}}>
        Click me {count}
      </button>
    </div>
    )
}

3. useEffect 副作用

什么是副作用呢,就是除了状态相关的逻辑,比如网络请求,监听事件,查找 dom

例如 组件在状态更新的时候改变 document.title

jsx
function App () {
  const [ count, setCount ] = useState(0)

  useEffect(() => {
    document.title = count
  })

  return (
    <div>
      页面名称: { count } 
      <button onClick={() => { setCount(count + 1 )}}>点我</button>
    </div>
    )
}

4. useContext 上下文对象

useContext 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。设计的目的就是解决组件树间数据传递的问题。

  • 用法
jsx
import React, { useContext, useState } from 'react';

const MyThemeContext = React.createContext({theme: 'light'}); // 创建一个上下文

function MyComponent() {
    const themeContext = useContext(MyThemeContext); // 使用上下文
    return (<div>{themeContext.theme}</div>);
}
  • 19 版本演示
jsx
import React, { useContext, useState } from 'react';
const ThemeContext = React.createContext<ThemeContextType>({} as ThemeContextType);
interface ThemeContextType {
   theme: string;
   setTheme: (theme: string) => void;
}

const Child = () => {
   const themeContext = useContext(ThemeContext);
   const styles = {
      backgroundColor: themeContext.theme === 'light' ? 'white' : 'black',
      border: '1px solid red',
      width: 100 + 'px',
      height: 100 + 'px',
      color: themeContext.theme === 'light' ? 'black' : 'white'
   }
   return <div>
      <div style={styles}>
         child
      </div>
   </div>
}

const Parent = () => {
   const themeContext = useContext(ThemeContext);
   const styles = {
      backgroundColor: themeContext.theme === 'light' ? 'white' : 'black',
      border: '1px solid red',
      width: 100 + 'px',
      height: 100 + 'px',
      color: themeContext.theme === 'light' ? 'black' : 'white'
   }
   return <div>
      <div style={styles}>
         Parent
      </div>
      <Child />
   </div>
}
function App() {
   const [theme, setTheme] = useState('light');
   return (
      <div>
         <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>切换主题</button>
         <ThemeContext value={{ theme, setTheme }}> 
            <Parent />
         <ThemeContext> 
      </div >
   );
}

export default App;

5. useRef 获取组件实例或者DOM元素

类似 vue3 ref

jsx
function TextInputWithFocusButton() {
  const inputEl = useRef(); /
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

6. useReducer 处理复杂状态

useState相似,useReducer也是 React 的 Hook,而且也只能放在组件最顶层使用。与前者不同的地方在于,它是通过 action 来更新状态的,使状态更新逻辑更具可读性。

jsx
const [state, dispatch] = useReducer(reducer, initialArg, init?)
  • useReducer接收三个参数:

    • reducer 函数:指定如何更新状态的还原函数,它必须是纯函数,以 state 和 dispatch 为参数,并返回下一个状态。
    • initialArg 初始状态:初始状态的计算值。
    • init(可选的)初始化参数:用于返回初始状态。如果未指定,初始状态将设置为 initialArg;如果有指定,初始状态将被设置为调用init(initialArg)的结果。
  • useReducer返回两个参数:

    • state 当前的状态:当前状态。在第一次渲染时,它会被设置为init(initialArg)或 initialArg(如果没有 init 的情况下)。
    • dispatch:调度函数,用于调用 reducer 函数,以更新状态并触发重新渲染。
示例
jsx
import React, { useReducer } from 'react';

const initialState = { count: 0 };

// Reducer 函数接收当前状态和动作,返回新的状态
function reducer(state, action) {
  switch (action.type) {
    case 'increment': 
      return { count: state.count + 1 };
    case 'decrement':
      return { 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>
    </>
  );
}

7. useMemo 性能优化

  • useMemo 接受两个参数:

    • 一个函数,这个函数返回需要记住的值。
    • 一个依赖项数组,当数组中的依赖项发生变化时,才会重新计算函数的返回值。
jsx
import React, { useMemo } from 'react';

function App() {
  const [count, setCount] = React.useState(0);

  const expensiveCalculation = useMemo(() => {
    console.log('计算中...');
    return count * 2;
  }, [count]);

  return (
    <div>
      <p>结果:{expensiveCalculation}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
    </div>
  );
}

export default App;

在这个例子中,我们使用 useMemo 对 count * 2 这个计算进行了优化。只有当 count 发生变化时,expensiveCalculation 的值才会重新计算。

8. useCallback 性能优化

  • useCallback 接受两个参数:

    • 一个函数,这个函数是我们需要记住的函数。
    • 一个依赖项数组,当数组中的依赖项发生变化时,才会重新创建新的函数。
jsx
import React, { useState, useCallback } from 'react';

function App() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('点击了按钮');
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>点击次数:{count}</p>
      <button onClick={handleClick}>增加</button>
    </div>
  );
}

export default App;

只有当 count 发生变化时,handleClick 函数才会被重新创建。

京ICP备2024093538号-1