【React Hooks 入门教程系列】:http://ourjs.com/wiki/view/react-hooks

React 函数组件本身不包含状态和生命周期,因此需要使用一些 Hooks (钩子)函数来实现状态保持、状态共享和状态传递

1. useState

可以通过 useState 为函数组件添加内部状态,组件会侦听 state 的状态变化并重新 render 组件

  1. // 代表 count 初始值为 0, 通过 setCount 改变 count 的值
  2. const [count, setCount] = useState(0);

2. useEffect

useEffect 可以用来侦听状态变化,并执行相应逻辑。如果需要在组件卸载时清理这些副效应,它允许 return 返回一个函数,在函数内做一些处理。实际使用中,由于副效应函数默认是每次渲染都会执行,所以清理函数不仅会在组件卸载时执行一次,每次副效应函数重新执行之前,也会执行一次,用来清理上一次渲染的副效应。

它的常见用途有下面几种:

  • 获取数据(data fetching)
  • 事件监听或订阅(setting up a subscription)
  • 改变 DOM(changing the DOM)
  • 输出日志(logging)

    2.1 无参数

    当 useEffect 无参数使用时,会侦听当前组件所有状态的变化,因此不带参数的 useEffect 函数中不能使用useState方法来改变state,否则会触发无限循环
    1. useEffect(() => { })

    2.2 带参数

    当 useEffect 带参数时,参数的变化才会触发 useEffect 的执行。此时可使用 useState
    1. useEffect(()=>{ setCount(count + 1) }, [isUpdate])

    2.3 空参数

    当 useEffect 传入空参数 [] 时,只有当第一次时执行。
    1. useEffect(()=>{ setCount(count + 1) }, [])

    3. useContext 和 createContext 使用,父子组件传递状态数据

    createContext 能够创建组件间共享的上下文状态。然后通过 useContext 在组件中使用这些状态

    3.1 createContext 用法

    1. // 只需要一个defaultValue默认值参数,可不填
    2. const MyContext = React.createContext(defaultValue)

    3.2 useContext 示例

    1) 创建一个全局 CountContext 共享计数器
    2) 通过 CountContext.Provider 向子组件传递状态 ```typescript import { useState, createContext, useContext } from “react”;

// 1. 创建一个全局 CountContext 共享计数器 const CountContext = createContext(0);

export function ExampleContext() { const [count, setCount] = useState(0); // 2. 通过 CountContext.Provider 向子组件传递状态 return (

useContext

); }

// 样例代码 function ExampleChild() { let count = useContext(CountContext); return (

ExampleChild: count in child {count}

); }

  1. <a name="Q4XmT"></a>
  2. ## 4. useReducer使用说明
  3. 结合useContext 实现 Redux 组件间共享状态管理,React Hooks 的 useReducer + useContext 已经基本可以实现类似 Redux 的状态管理。<br />useReducer 是 useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。机制与 Redux 类似。
  4. <a name="Lt7kP"></a>
  5. ### 4.1 基本用法
  6. 在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。
  7. ```typescript
  8. const [state, dispatch] = useReducer(reducer, initialArg, init);

4.2 useReducer 简单示例

下面这个示例,我们在一个对象中定义了两个计数器,通过传递不同的参数改变相应的值。当点击 :”Add IP” 时,countInfo 中的 IP/PV 值都加1,当点击 “Add PV”时,只有 pv 值加1。

  1. import { useReducer } from "react";
  2. // 定义一个CountReducer
  3. function CountReducer(state: { pv: number; ip: number; }, action: "pv" | "ip") {
  4. switch (action) {
  5. case "pv":
  6. return { ...state, ip: state.pv + 1 };
  7. case "ip":
  8. return { ...state, ip: state.ip + 1, pv: state.pv + 1 };
  9. }
  10. return state;
  11. }
  12. // 样例代码
  13. export function ExampleReducer() {
  14. const [countInfo, dispatch] = useReducer(CountReducer, { ip: 0, pv: 0 });
  15. return (
  16. <div>
  17. <h1>useReducer: countInfo {JSON.stringify(countInfo)}</h1>
  18. <button onClick={() => dispatch("ip")}>Add IP</button>
  19. <button onClick={() => dispatch("pv")}>Add PV</button>
  20. </div>
  21. );
  22. }

5. useReducer 结合useContext 实现 Redux 组件间共享状态管理

使用 useContext 创建的对象负责数据的管理,也即是数据的共享。而使用 useReducer 负责共享数据的更新,每一次数据更新会导致页面重新渲染。

限于篇幅就不放这部分代码了,思路如下:
1) 创建一个全局 UserContext 共享数据上下文
2) 通过 UserContext.Provider 向子组件传递状态
3) 使用 UserContext 获得 value 传入的对象,子组件获得带有数据的 store 和 dispatch

6. useMemo/useCallback使用,useEffect渲染生命周期执行顺序区别

useMemo ,【创建】一个依赖函数。当其中一个依赖项更改时, useMemo 重新计算记忆的值。而不需要在每个渲染进行昂贵的计算。

6.1 useMemo

useMemo作用
useMemo 其实创建了一种数据缓存机制。比如登录页面需要向后台发送含用户名&密码的 ajax 请求,获取用户登录信息。useMemo 可以设置成只有当用户名密码改变后才向后台重新发送 ajax,而在组件重新渲染时使用缓存的用户信息。

useMemo 与 useEffect 生命周期
useMemo 与 useEffect 作用类似,都会在依赖值改变时重新执行,但 useMemo 有一个缓存的返回值。因此在组织渲染生命周期中,很自然地会把 useMemo 放到渲染 DOM 之前执行。
useMemo 在 DOM 渲染之前执行的,而 useEffect 则在 DOM 渲染之后执行: useMemo => 渲染DOM => useEffect

useMemo 用法

  1. import { useMemo } from "react";
  2. let memoIp = useMemo(() => {
  3. return "useMemo ip here:" + Date.now();
  4. }, [ip]);
  5. return (
  6. <div>
  7. <h1>
  8. useMemoEffect: IP:{ip}, PV:{pv}
  9. </h1>
  10. <p>{memoIp}</p>
  11. <button
  12. onClick={() => {
  13. setIP(ip + 1);
  14. }}
  15. >
  16. Add IP
  17. </button>
  18. <button
  19. onClick={() => {
  20. setPV(pv + 1);
  21. }}
  22. >
  23. Add PV
  24. </button>
  25. </div>
  26. );

6.2 useCallback

useMemo 和 useCallback 接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于useMemo 返回的是函数运行的结果,useCallback 返回的是函数。

useCallback(fn, deps) 与 useMemo(() => fn, deps) 是等价的,相当于反函数存起来,由用户决定何时使用。

7. useRef 使用与 createRef 的区别

useRef:引用当前组件的DOM对象,返回一个引用了DOM的对象,返回的对象将在组件的整个生存期内持续存在。

useRef 使用

  1. import React, { useRef } from "react";
  2. export function ExampleRef() {
  3. let inputRef = useRef();
  4. let setInputValue = function () {
  5. if(inputRef.current && inputRef.current.value ) {
  6. inputRef.current.value = "OurJS:" + Date.now();
  7. }
  8. };
  9. return (
  10. <div>
  11. <h1>useRef</h1> <input type="text" ref={inputRef} />
  12. <button onClick={setInputValue}>Set Value:useRef</button>
  13. </div>
  14. );
  15. }

与 createRef 区别
createRef 每次渲染都会返回一个新的引用,而 useRef 每次都会返回相同的引用,因此你可以在 useRef 对象上存放缓存的值。