image.png

useState

模拟实现

  • 因为是根据数组的索引来储存数据,所以不能在 if 里使用 useState,这样会导致数据顺序错乱。

    1. let _state = [] // 将设置的值保存在函数外
    2. let _index = 0 // 储存多个值
    3. function myUseState(initialValue) {
    4. const currentIndex = index
    5. index += 1
    6. _state[currentIndex] = _state[currentIndex] === undefined ? initialValue : _state // 没设置值的时候取初始值
    7. const setState = (newValue) => {
    8. _state = newValue
    9. ReactDOM.render(
    10. index = 0
    11. <App/>,document.getElementById('root')
    12. ) // 重新渲染
    13. }
    14. return [_state[currentIndex], setState] // 返会 值和 setter 函数
    15. }

    App 组件的更新过程

    Snipaste_2021-02-23_20-39-20.png

    注意事项

  • 每次重新渲染,函数会重新执行,对应的 state 会出现分身。如果不希望出现可以使用 useRefuseContext

    useRef

    作用

  • useRef 返回一个可变的 ref 对象,.current 该对象的属性已初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内保持不变。

  • 本质上,useRef 就像一个“盒子”,可以在其 .current 属性中保存可变的值。

    语法

    1. function App() {
    2. const value = useRef(0)
    3. const onClick = () => value.current += 1 // current 为了保证不同组件使用的是同一个值(引用)
    4. useEffect(() => { // 挂载后执行 log value
    5. console.log(value.current) // <input type="text">
    6. })
    7. return (
    8. <>
    9. <input ref={value} type="text"/>
    10. <button onClick={onClick}>+1</button>
    11. </>
    12. )

    注意事项

  • 不会触发 UI 更新

  • 不能将 ref 传递给子组件。因为 props 中没有 ref,此时需要使用 forwarfRef API

    forWardRef

    作用

  • 创建一个 React 组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中

    语法

    1. const FancyButton = React.forwardRef(
    2. (props, ref) => (<div ref={ref} >xxx</div>)
    3. );
    4. // 现在可以直接获得 DOM 的引用:
    5. const ref = useRef(null);
    6. <FancyButton ref={ref}>Click me!</FancyButton>;

    useImperativeHandle

    作用

  • 在使用 ref 时自定义暴露给父组件的实例值。

  • 在大多数情况下,应当避免使用 ref 这样的命令式代码。应当与 forwardRef 一起使用

    语法

  • 应该叫做 setRef

  • useImperativeHandle( ref, createHandle, [deps] )

    示例

  • Child 的父组件可以调用 inputRef.current.focus()

    1. function Child(props, ref) {
    2. const inputRef = useRef();
    3. useImperativeHandle(ref, () => ({ // 修改 ref
    4. focus: () => { // 在 ref 上增加 focus 方法
    5. inputRef.current.focus();
    6. },
    7. inputRef:inputRef // 原来的 ref 值
    8. }));
    9. return <input ref={inputRef} ... />;
    10. }
    11. FancyInput = forwardRef(FancyInput);

    useEffect

  • Effect(副作用):改变当前了环境,数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用。

    作用

  • 通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式的 API。

    对应类组件中的生命周期

    ```javascript useEffect( () => {console.log(1)},[] ) ===== componentDidMount // 只在挂载时触发

useEffect( () => {console.log(1)},[n] ) ===== componentDidUpdate
// 第一次也会执行,只在 n 更新时更新,不写表示监听 state props

useEffect( () => { ===== componentWillUnmount // 组件消亡时触发 console.log(1) return () => {} } )

  1. <a name="hwSXI"></a>
  2. ### 注意事项:在组件内部调用 useEffect
  3. - 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存在函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。
  4. <a name="marAp"></a>
  5. # useLayoutEffect
  6. <a name="aYXQq"></a>
  7. ### 作用
  8. - 在 `render` 之前调用,执行时机早于 `useEffect`。先用 `useEffect`,只有当它出问题的时候再尝试使用 `useLayoutEffect`。
  9. <a name="SjEFF"></a>
  10. ### 注意事项
  11. - 会影响页面渲染速度,最好只在改变 Layout 时使用
  12. <a name="c4683"></a>
  13. # useContext
  14. <a name="mcncU"></a>
  15. ### 语法
  16. - 接收一个 context 对象(`React.createContext` 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 `<MyContext.Provider>` 的 `value` 属性 决定。
  17. - **context:上下文(程序运行时所有可用的变量),局部的全局变量**
  18. - 当组件上层最近的 `<MyContext.Provider>` 更新时,该 Hook 会触发重渲染,并使用最新传递给 `MyContext` provider 的 context `value` 值。即使祖先使用 [`React.memo`](https://zh-hans.reactjs.org/docs/react-api.html#reactmemo) 或 [`shouldComponentUpdate`](https://zh-hans.reactjs.org/docs/react-component.html#shouldcomponentupdate),也会在组件本身使用 `useContext` 时重新渲染。
  19. <a name="FxYg8"></a>
  20. ### 使用流程
  21. 1. 使用` C= createcontext(initial)` 创建上下文
  22. 1. 使用 `<C.provider></C.Provider>` 圈定作用域
  23. 1. 在作用域内使用 `useContext(C)` 来使用上下文
  24. <a name="05ZyJ"></a>
  25. ### 作用
  26. - 可在祖先、后代组件中传递数据
  27. <a name="57jOM"></a>
  28. ### 注意事项
  29. - 不是响应式
  30. <a name="WN42v"></a>
  31. ### 示例
  32. - 示例一:提供 set 数据
  33. ```javascript
  34. const themes = {
  35. light: {
  36. foreground: "#000000",
  37. background: "#eeeeee"
  38. },
  39. dark: {
  40. foreground: "#ffffff",
  41. background: "#222222"
  42. }
  43. };
  44. const ThemeContext = React.createContext(themes.light); // 创建
  45. function App() {
  46. return (
  47. <ThemeContext.Provider value={themes.dark}> // 在此作用域中可以使用 useContext
  48. <Toolbar />
  49. </ThemeContext.Provider>
  50. );
  51. }
  52. function Toolbar(props) { // 子组件
  53. return (
  54. <div>
  55. <ThemedButton />
  56. </div>
  57. );
  58. }
  59. function ThemedButton() { // 孙子组件
  60. const theme = useContext(ThemeContext); // 注入(使用),值为最近的<ThemeContext.Provider> 的值
  61. return (
  62. <button style={{ background: theme.background, color: theme.foreground }}>
  63. 使用主题
  64. </button>
  65. );
  66. }
  • 示例二:提供 set、get 数据 ```javascript const ThemeContext = React.createContext(null) // 创建 function App() { // 祖先组件
    const [theme, setTheme] = useState({ light: {
    1. foreground: '#000000',
    2. background: '#eeeeee'
    }, }) return ( // 提供 set、get
    1. <Toolbar/>
    ) }

function Toolbar(props) { // 子组件 return (

) }

function ThemedButton() { // 孙子组件 const {theme, setTheme} = useContext(ThemeContext) // 注入(使用),值为最近的 的值 return ( <> </> ) }

  1. <a name="1AJNb"></a>
  2. # useReducer
  3. <a name="1RRG9"></a>
  4. ### 语法
  5. - useReducer 接收一个形如 `(state, action) => newState` 的 reducer,并返回当前的 state 以及与其配套的 `dispatch` 方法。
  6. <a name="cSqwr"></a>
  7. ### 作用
  8. - state 逻辑较复杂且包含多个子值,或下一个 state 依赖于之前的 state 等情况,`useReducer` 比 `useState` 更适用。
  9. ```javascript
  10. const initialState = {count: 0};
  11. function reducer(state, action) { // 触发时会将会执行 reducer,并将参数和初始值穿过来
  12. switch (action.type) {
  13. case 'increment':
  14. return {count: state.count + 1};
  15. case 'decrement':
  16. return {count: state.count - 1};
  17. default:
  18. throw new Error();
  19. }
  20. }
  21. function Counter() {
  22. const [state, dispatch] = useReducer(reducer, initialState);
  23. // 接受 reducer 和初始值,返回 state 和 触发 Api
  24. return (
  25. <>
  26. Count: {state.count}
  27. <button onClick={() => dispatch({type: 'decrement'})}>-</button> // 接受一个对象传给reducer
  28. <button onClick={() => dispatch({type: 'increment'})}>+</button>
  29. </>
  30. );
  31. }

memo

作用

  • 如果 props 没有改变,会跳过渲染并直接复用最近一次渲染的结果,避免重复渲染

    语法

  • const fn = memo(() => {} ) 接受一个组件函数

    示例

  • 不使用 memo 时,n 变化时 App 组件的所有子组件都会重新渲染 ```javascript function App() { const [n, setN] = useState(0) const [m, setm] = useState(0) const add = () => setN(n + 1) return (

    1. <div>
    2. <button onClick={add}>+1</button>
    3. </div>
    4. <Child data={m}/>

    ) }

/ function Child(props) { console.log(‘child 渲染了’) return

{props.m}
} / const Child = memo(function (props) { console.log(‘child 渲染了’) return
{props.m}
})

  1. <a name="SZf5z"></a>
  2. ### 注意事项
  3. - 当子组件 props 中有函数,父组件重新渲染时会重新生成函数,此时函数地址变化,会导致 memo 失效,需使用 [**useMemo **](#hTQyT)API
  4. <a name="hTQyT"></a>
  5. # useMemo
  6. <a name="8TrtL"></a>
  7. ### 作用
  8. - 把“创建”函数和依赖项数组作为参数传入 `useMemo`,它会在有依赖项改变时才重新计算 memoized(记忆的) 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
  9. <a name="SHRuQ"></a>
  10. ### 语法
  11. - `const fn = memo(() => (x)=>{},[xxx])` 接受一个函数和依赖项数组
  12. ```javascript
  13. function App() {
  14. const [n, setN] = useState(0)
  15. const [m, setm] = useState(0)
  16. const add = () => setN(n + 1)
  17. //const fn = () => console.log(123) 使用 useMemo
  18. const fn = useMemo(() => () => console.log(123), [m])
  19. return (
  20. <div>
  21. <div>
  22. <button onClick={add}>+1</button>
  23. </div>
  24. <Child data={m} fn={fn()}/>
  25. </div>
  26. )
  27. }
  28. const Child = memo((props) => {
  29. console.log('child 渲染了')
  30. return <div onClick={props.fn}>{props.data}</div>
  31. })

注意事项

  • 语法过于复杂,于是创造了语法糖 useCallback

    useCallback

    1. useCallback( fn, deps ) 等同于 useMemo( () => fn, deps )