# classicHooks

back:hooks大全

以下是我们将要使用的 Hooks API,基本上都会用到:

# Hooks配方

back

# useLayoutEffect

back

这个钩子函数和useEffect相同,都是用来执行副作用。但是它会在所有的DOM变更之后同步调用effect。useLayoutEffect和useEffect最大的区别就是一个是同步一个是异步。

  • useEffect,使用useEffect不会阻塞浏览器的重绘
  • useLayoutEffect, 使用useLayoutEffect,会阻塞浏览器的重绘。如果你需要手动的修改Dom,推荐使用useLayoutEffect。因为如果在useEffect中更新Dom,useEffect不会阻塞重绘,用户可能会看到因为更新导致的闪烁

# 自定义hook

自定义Hooks | back

# useImperativeMethods

back

接受一个 ref 作为参数,内部其实就是一个 useLayoutEffect 的调用。 主要就是在外部传入的 ref 上挂载内容 ,实现类似 ref 挂载到 ClassComponent 上的效果

function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeMethods(ref, () : ({
    focus: () : {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
1
2
3
4
5
6
7
8
9
10

# useCallback

back

  • 创建一个回调方法的缓存,可以让我们传入子节点作为 props 的时候,可以让其没有变化,避免没必要的渲染。
  • 根据输入的 inputs,也就是一个数组,内部的内容是否又变回,决定是返回存储的老方法,还是返回新的方法并记录。
  • useCallback会返回memoized函数。当依赖项改变的时候,会返回的新的memoized函数。
import React, { useState, useCallback, useEffect } from "react";

const set = new Set();

function Callback() {
  const [count, setCount] = useState(1);
  const [val, setVal] = useState("");

  const callback = useCallback(() => {
    console.log(count);
  }, [count]);
  set.add(callback);

  return (
    <div>
      <h4>{count}</h4>
      <h4>{set.size}</h4>
      <div>
        <button onClick={() => setCount(count + 1)}>+</button>
        <input value={val} onChange={event => setVal(event.target.value)} />
      </div>
    </div>
  );
}

export default function Parent() {
  const [count, setCount] = useState(1);
  const [val, setVal] = useState("");

  const callback = useCallback(() => {
    return count;
  }, [count]);
  return (
    <div>
      <h4>{count}</h4>
      <Child callback={callback} />
      <div>
        <button onClick={() => setCount(count + 1)}>+</button>
        <input value={val} onChange={event => setVal(event.target.value)} />
      </div>
    </div>
  );
}

function Child({ callback }) {
  const [count, setCount] = useState(() => callback());
  useEffect(() => {
    setCount(callback());
  }, [callback]);
  return <div>{count}</div>;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  • useMemo 与 useCallback 类似,都是有着缓存的作用。本质的区别可能就是:一個緩存函數、一個緩存值

# React.useRef

back

useRef 的作用: 获取 DOM 元素的节点 获取子组件的实例 渲染周期之间共享数据的存储(state 不能存储跨渲染周期的数据,因为 state 的保存会触发组件重渲染)

import React, { useEffect, useRef } from "react";
function App() {
  const h1Ref = useRef();
  useEffect(() => {
    console.log("useRef");
    console.log(h1Ref.current);
  }, []);
  return <h1 ref={h1Ref}>Hello World!</h1>;
}
export default App;
1
2
3
4
5
6
7
8
9
10

useEffect 里面使用到的 state 的值, 固定在了 useEffect 内部, 不会被改变,除非 useEffect 刷新,重新固定 state 的值:

有办法解决的,就是用 useRef,可以理解成 useRef 的一个作用:就是相当于全局作用域,一处被修改,其他地方全更新...

  • 普遍操作,用来操作 dom
const Hook = () => {
  const [count, setCount] = useState(0);
  const btnRef = useRef(null);

  useEffect(() => {
    console.log("use effect...");
    const onClick = () => {
      setCount(count + 1);
    };
    btnRef.current.addEventListener("click", onClick, false);
    return () => btnRef.current.removeEventListener("click", onClick, false);
  }, [count]);

  return (
    <div>
      <div>{count}</div>
      <button ref={btnRef}>click me </button>
    </div>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 如何在funtion组件中,打印出新值

2020-03-11_135203.gif

# useMemo

back

只要父组件的状态更新,无论有没有对自组件进行操作,子组件都会进行更新,useMemo就是为了防止这点而出现的

  • React 16.8.0 中发布useMemo():useMemo 作为一个有着暂存能力的
  • React.memo()是判断一个函数组件的渲染是否重复执行
  • useMemo()是定义一段函数逻辑是否重复执行
  • useMemo(() => fn, inputs)跟 useCallback(fn, inputs)效果一样
  • useMemo和useCallback类似。useMemo会返回memoized值。当依赖项改变时,会重新计算memoized值。
const increase = useMemo(() => {
    if(value > 2) return value + 1;
}, [value]);
]
1
2
3
4
const Child = memo(({ data }) => {
  console.log("child render...", data.name);
  return (
    <div>
      <div>child</div>
      <div>{data.name}</div>
    </div>
  );
});

const Hook = () => {
  console.log("Hook render...");
  const [count, setCount] = useState(0);
  const [name, setName] = useState("rose");

  const data = useMemo(() => {
    return {
      name
    };
  }, [name]);
  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount(count + 1)}>update count </button>
      <Child data={data} />
    </div>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  • 首先,memo 的用法是:函数组件里面的 PureComponent

但是,如果函数组件被 React.memo 包裹,且其实现中拥有 useState 或 useContext 的 Hook,当 context 发生变化时,它仍会重新渲染。

  • 而且,memo是浅比较,意思是,对象只比较内存地址,只要你内存地址没变,管你对象里面的值千变万化都不会触发 render

# useReducer

back

Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态

  • 简单来说 reducer是一个函数(state, action) => newState: 接收当前应用的 state 和触发的动作 action,计算并返回最新的 state。
  • useReducer一共可以接受三个参数并返回当前的state与其配套的dispatch。
import React, { useReducer } from "react";
function reducer(state, action) {
  switch (action.type) {
    case "add":
      return { count: state.count + 1 };
    case "sub":
      return { count: state.count - 1 };
    default:
      return state;
  }
}
function Example2() {
  const initialState = { count: 0 };
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <p>You clicked {state.count} times</p>
      <button
        onClick={() => {
          dispatch({ type: "add" });
        }}
      >
        add
      </button>
      <button
        onClick={() => {
          dispatch({ type: "sub" });
        }}
      >
        sub
      </button>
    </div>
  );
}
export default Example2;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  • useReducer的第三个参数接受一个函数作为参数,并把第二个参数当作函数的参数执行。主要作用是初始值的惰性求值,把一些对状态的逻辑抽离出来,有利于重置state。
function init(initialCount) {
    return [
        ...initialCount,
    ];
}


useReducer(reducer,[
        {
            id: Date.now(),
            value: "Hello react"
        }
    ],init)
1
2
3
4
5
6
7
8
9
10
11
12
13

# React.useEffect

back

# 超详细userEffect

使用 useEffect,可以直接在函数组件内处理生命周期事件。 如果你熟悉 React class 的生命周期函数,
你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate和 componentWillUnmount 这三个函数的组合

import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";

function useEffectTest() {
  useEffect(() => {
    // 默认情况下,每次渲染后都会调用该函数
    console.log("render!");

    // 如果要实现 componentWillUnmount,
    // 在末尾处返回一个函数
    // React 在该函数组件卸载前调用该方法
    // 其命名为 cleanup 是为了表明此函数的目的,
    // 但其实也可以返回一个箭头函数或者给起一个别的名字。
    return function cleanup() {
      console.log("unmounting...");
    };
  });
  return "useEffectTest";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

阻止每次重新渲染都会执行 useEffect 如果希望 effect 较少运行,可以提供第二个参数 - 值数组。 将它们视为该 effect 的依赖关系。 如果其中一个依赖项自上次更改后,effect 将再次运行。

const [value, setValue] = useState(0);

useEffect(() => {
  // 仅在 value 更改时更新
  console.log(value);
}, [value]);
1
2
3
4
5
6
  • 如果我们之前订阅了什么,最后在willUnMount这个生命周期里面要取消订阅,这可咋用 useEffect 实现啊:
useEffect(() => {
    const subscription = 订阅全国人民吃饭的情报!
    return () => {
        取消订阅全国人民吃饭的情报!
    }
},[])
1
2
3
4
5
6

大家都知道,render 了之后会执行重新 useEffect,如果 useEffect 里面有一个每 setInterval...那么每次 render 了,再次执行 useEffect 就会再创建一个 setInterval,然后就混乱了...可以把下面案例 return 的内容删掉感受下

# useEffect 的一些暗戳戳的规则

  • useEffect 里面使用到的 state 的值, 固定在了 useEffect 内部, 不会被改变,除非 useEffect 刷新,重新固定 state 的值
  • useEffect 不能被判断包裹
  • useEffect 不能被打断
const [count, setCount] = useState(0)
useEffect(...)

return // 函数提前结束了

useEffect(...)
}
1
2
3
4
5
6
7
  • useEffec函数参数中返回函数所代表的销毁是useEffect自己的销毁,每次重新执行函数组件都会重新生成新的Effect。假如没有销毁,由于useEffect的函数参数会在首次渲染和更新的时候调用,这就有了一致命的缺点:如果我是定义的事件,每次更新都会执行,那么岂不是在事件还没有移除掉又定义了一次,所以useEffect加入了这个功能。

# 是什么差异呢

为什么有时候在effect里拿到的是旧的state或prop呢?

这是因为javascript闭包的机制,函数组件被调用后,函数内部的state由于被内部定时器的回调所依赖,所以没有被垃圾回收机制所清除,而是保存在内存里了。所以等定时器的回掉执行时,打印的是之前闭包中存储的变量。

  • 如何避免拿到旧的prop或者state?
    • 使用useRef
    • 查看是否遗漏了依赖, 导致effect没有更新

# 如何在useEffect中使用async/await

async函数默认会返回一个Promise对象,而useEffect中,只允许什么都不返回或者返回一个清除函数。控制台中,会触发如下警告
Warning: useEffect function must return a cleanup function or nothing. Promises and useEffect(async () => …) are not supported, but you can call an async function inside an effect..

useEffect(() => {
  const fetchData = async () => {
    const result = await axios('http://---');
    setData(result);
  }
  fetchData();
}, []);
1
2
3
4
5
6
7

# 依赖数组的问题

官网 官网2

string,number,undefined,null 他的值都是能够正常检测的,问题的关键还是在于 objectfunction

  • object
    • 当依赖值为 object 时,他的值引用发生变化就会触发 effect 的更新
    • 但是如果只是对象里某个值变化而引用不变化,effect 依旧不会触发
  • 关于 function 的试验结论:
    • 如果初始值为 function, 而将其改为数字等,会触发 effect
    • function 也是一个对象,当我们添加一个值在上面时,他的 effect 也不会触发
    • 引用为一个新函数时,他每次都会触发;

# useEffect注意事项

  • 可以使用多个useEffect分离逻辑。在class组件中,我们通常会在componentDidMount中,混杂添加事件绑定,请求数据等多种无关的逻辑。
  • 如果只需要useEffect在加载时执行一次,或者卸载组件时执行一次,可以向useEffect传递一个空数组。
  • useEffect不支持直接使用async函数
  • 如果在useEffect中形成了闭包,将会拿到旧的props,或者state。(这个与Hook无关,与函数组件本身的特性相关)
  • useEffect返回的函数,清除函数。会在上一个effect被清除时调用。

# useContext

back

useContext 可以实现共享状态最大的改变是可以在使用 Counter 的时候不必在包裹 Children 了,比方说我们先创建一个上下文, 这个上下文里头有一个名为 count 的 state,以及一个修改 count 的方法 setCount
React16中更新了Context API,Context主要用于爷孙组件的传值问题,新的Context API使用订阅发布者模式方式实现在爷孙组件中传值。

import React, { createContext, useState } from "react";
import { Counter } from "./Counter";
export const CountContext = createContext();
function Example2() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <CountContext.Provider value={(count, setCount)}>
        <Counter />
      </CountContext.Provider>
    </div>
  );
}
export default Example2;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { useContext} from 'react';
import {CountContext} from './Example2'
export function Counter() {
    const {count,setCount} = useContext(CountContext)  //一句话就可以得到count
    return (
        <h2>{count}</h2>
        <button onClick={() => { setCount(count + 1) }}>count</button>
    )
}
1
2
3
4
5
6
7
8
9

提示:想要通过useContext中获得存入的数据,子组件或者孙组件需要被该父组件包裹起来

# React.useState

back

useState 是 react 自带的一个 hook 函数,它的作用就是用来声明状态变量。
useState 这个函数接收的参数是我们的状态初始值(initial state)(只在第一次有效),
它返回了一个数组,这个数组的第[0]项是当前当前的状态值, 第[1]项是可以改变状态值的方法函数。

我们可以把这里的 state 看做我们在 class component 中使用的 this.state。

我们的每一次 setState 操作,在改变了值之后,都会引发 rerender 操作,从而触发页面的更新(但是如果没有改变的话,则不会触发 rerender,我们在后面将会利用这一特性做一件有趣的事情)。
更新state变量的方法,并不会像this.setState一样,合并state。而是替换state变量

import React, { useState, useEffect } from "react";

interface Props {
  value: number;
  onChange: (num: number) => any;
}

export default function Counter({ value, onChange }: Props) {
  const [count, setCount] = useState<number>(0);

  useEffect(() => {
    value && setCount(value);
  }, [value]);

  return [
    <div key="a">{count}</div>,
    <button key="b" onClick={() => onChange(count + 1)}>
      点击+1
    </button>
  ];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { useState } from "react";
function Example() {
  //useState的用法,分别是声明、读取、使用(修改)
  const [count, setCount] = useState(0); //数组结构
  const [age, setAge] = useState(18);
  const [todos, setTodos] = useState([{ text: "Learn Hooks" }]);
  return (
    <div>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        count
      </button>
      <button
        onClick={() => {
          setAge(age + 1);
        }}
      >
        age
      </button>
    </div>
  );
}
export default Example;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function MyComponent({ bigJsonData }) {
  const [value, setValue] = useState(function getInitialState() {
    const object = JSON.parse(bigJsonData); // expensive operation
    return object.initialValue;
  });

  // ...
}
1
2
3
4
5
6
7
8

# useState函数式更新

如果新的state需要之前的state计算得到。可以向useState返回的更新函数中,传递一个函数。函数的参数是前一个state。

setValue( preValue => preValue + 1;)
1

# useState惰性的初始值

useState的初始值是惰性的,只会在初次渲染组件的时候起作用。如果state的初始值需用通过复杂计算得到,useState的初始值也可以是一个函数,函数的返回值将是useState的初始值。

const [value,setValue]=useState(() => {return parseInt(Math.random()*10,10)});
1

# react-redux@7.1.0 useSelector: 别啦 connect

back

const result : any = useSelector(selector : Function, equalityFn? : Function);
//就是从redux的store对象中提取数据(state)。
1
2

注意: 因为这个可能在任何时候执行多次,所以你要保持这个 selector 是一个纯函数。

这个 selector 方法类似于之前的connect的mapStateToProps参数的概念。并且 useSelector 会订阅 store, 当 action 被 dispatched 的时候,会运行 selector。
当然,仅仅是概念和 mapStateToProps 相似,但是肯定有不同的地方,看看 selector 和 mapStateToProps 的一些差异:

  • selector 会返回任何值作为结果,并不仅仅是对象了。然后这个 selector 返回的结果,就会作为 useSelector 的返回结果。
  • 当 action 被 dispatched 的时候,useSelector()将对前一个 selector 结果值和当前结果值进行浅比较。如果不同,那么就会被 re-render。  反之亦然。
  • selector 不会接收 ownProps 参数,但是,可以通过闭包(下面有示例)或使用柯里化 selector 来使用 props。
  • 使用记忆(memoizing) selector时必须格外小心(下面有示例)。
  • useSelector()默认使用===(严格相等)进行相等性检查,而不是浅相等(==)。

你可能在一个组件内调用 useSelector 多次,但是对 useSelector()的每个调用都会创建 redux store 的单个订阅。由于 react-reduxv7 版本使用的 react 的批量(batching)更新行为,造成同个组件中,多次 useSelector 返回的值只会 re-render 一次。

  • useSelector 用于从 Redux 存储的 state 中提取值并订阅该 state,这基本上类似于在 hooks 中实现的 mapStateToProps 函数
  • 首先,不再提供 ownProps API,并且应该使用 useCallback 或 useMemo 来通过自定义逻辑获取它们
  • 其次,useSelector()第二个参数也是依赖数组,跟 useEffect 一样。如果不提供第二个参数每次组件更新时都会重新计算;如果提供了依赖数组,只有依赖数组对应的值变更了之后,才会触发重新计算
  • 除此之外,redux 以前的性能优化逻辑同样保留了下来,如果当前的 props 跟老的 props 相同,则组件将不会重新渲染。
  • 由于 React redux 中使用的批处理更新的逻辑,导致同一组件中的多个 useSelector()重新计算出 state,只会让组件重新渲染一次。因此,我们可以自由的在组件中 useSelector(),而不用担心重复渲染的情况
  • 在下面的例子中,我们可以将单个 useSelector()分成两个独立的(一个读取 title,另一个读取 content)useSelector()他们在性能和渲染数量方面完全相同
import React from 'react';
import { useSelector } from 'react-redux';
const Component = props => {
  const { title, content } = useSelector(state => ({
      title: state.title,
      content: state.content
  }));

  return <div title={title}>{content}</div>;
1
2
3
4
5
6
7
8
9

# 相等比较和更新

back

当函数组件渲染时,会调用提供的 selector 函数,并且从 useSelector 返回其结果。(如果 selector 运行且没有更改,则会返回缓存的结果)。

v7.1.0-alpha.5开始,默认比较是严格比较(===)。这点于 connect 的时候不同,connect 使用的是浅比较。这对如何使用 useSelector()有几个影响。
使用 mapState,所有单个属性都在组合对象中返回。返回的对象是否是新的引用并不重要 - connect()只比较各个字段。使用 useSelector 就不行了,默认情况下是,如果每次返回一个新对象将始终进行强制 re-render。如果要从 store 中获取多个值,那你可以这样做:

  • useSelector()调用多次,每次返回一个字段值。
  • 使用Reselect或类似的库创建一个记忆化(memoized) selector,它在一个对象中返回多个值,但只在其中一个值发生更改时才返回一个新对象。
  • 使用 react-redux 提供的shallowEqual函数作为useSelector的equalityFn参数
import { shallowEqual, useSelector } from "react-redux";
// later
const selectedData = useSelector(selectorReturningObject, shallowEqual);
1
2
3

# useSelector 例子

back

import React from "react";
import { useSelector } from "react-redux";

export const CounterComponent = () => {
  const counter = useSelector(state => state.counter);
  return <div>{counter}</div>;
};
1
2
3
4
5
6
7

通过闭包使用props来确定要提取的内容

import React from "react";
import { useSelector } from "react-redux";

export const TodoListItem = props => {
  const todo = useSelector(state => state.todos[props.id]);
  return <div>{todo.text}</div>;
};
1
2
3
4
5
6
7

# 使用记忆化_memoizing_selector

back

当使用如上所示的带有内联selector的useSelector时,如果渲染组件,则会创建 selector 的新实例。只要 selector 不维护任何状态,这就可以工作。但是,记忆化(memoizing) selectors 具有内部状态,因此在使用它们时必须小心

selector仅依赖于状态时,只需确保它在组件外部声明,这样一来,每个渲染所使用的都是相同的选择器实例:

import React from "react";
import { useSelector } from "react-redux";
import { createSelector } from "reselect"; //上面提到的reselect库

const selectNumOfDoneTodos = createSelector(
  state => state.todos,
  todos => todos.filter(todo => todo.isDone).length
);

export const DoneTodosCounter = () => {
  const NumOfDoneTodos = useSelector(selectNumOfDoneTodos);
  return <div>{NumOfDoneTodos}</div>;
};

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <DoneTodosCounter />
    </>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

如果selector依赖于组件的props,但是只会在单个组件的单个实例中使用,则情况也是如此:

import React from "react";
import { useSelector } from "react-redux";
import { createSelector } from "reselect";

const selectNumOfTodosWithIsDoneValue = createSelector(
  state => state.todos,
  (_, isDone) => isDone,
  (todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
);

export const TodoCounterForIsDoneValue = ({ isDone }) => {
  const NumOfTodosWithIsDoneValue = useSelector(state =>
    selectNumOfTodosWithIsDoneValue(state, isDone)
  );

  return <div>{NumOfTodosWithIsDoneValue}</div>;
};

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <TodoCounterForIsDoneValue isDone={true} />
    </>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

但是,如果selector被用于多个组件实例并且依赖组件的props,那么你需要确保每个组件实例都有自己的 selector 实例

import React, { useMemo } from "react";
import { useSelector } from "react-redux";
import { createSelector } from "reselect";

const makeNumOfTodosWithIsDoneSelector = () =>
  createSelector(
    state => state.todos,
    (_, isDone) => isDone,
    (todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
  );

export const TodoCounterForIsDoneValue = ({ isDone }) => {
  const selectNumOfTodosWithIsDone = useMemo(
    makeNumOfTodosWithIsDoneSelector,
    []
  );

  const numOfTodosWithIsDoneValue = useSelector(state =>
    selectNumOfTodosWithIsDoneValue(state, isDone)
  );

  return <div>{numOfTodosWithIsDoneValue}</div>;
};

export const App = () => {
  return (
    <>
      <span>Number of done todos:</span>
      <TodoCounterForIsDoneValue isDone={true} />
      <span>Number of unfinished todos:</span>
      <TodoCounterForIsDoneValue isDone={false} />
    </>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# useSelector 性能

back

前面说了,selector 的值改变会造成 re-render。但是这个与 connect 有些不同,useSelector()不会阻止组件由于其父级 re-render 而 re-render,即使组件的 props 没有更改。
如果需要进一步的性能优化,可以在 React.memo()中包装函数组件:

const CounterComponent = ({ name }) => {
  const counter = useSelector(state => state.counter);
  return (
    <div>
      {name}: {counter}
    </div>
  );
};

export const MemoizedCounterComponent = React.memo(CounterComponent);
1
2
3
4
5
6
7
8
9
10

# useDispatch()

back

const dispatch = useDispatch();
//这个Hook返回Redux store中对dispatch函数的引用。你可以根据需要使用它。
1
2
import React from "react";
import { useDispatch } from "react-redux";

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch();

  return (
    <div>
      <span>{value}</span>
      <button onClick={() => dispatch({ type: "increment-counter" })}>
        Increment counter
      </button>
    </div>
  );
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

当使用dispatch将回调传递给子组件时建议使用useCallback对其进行记忆,否则子组件可能由于引用的更改进行不必要地呈现。

import React, { useCallback } from "react";
import { useDispatch } from "react-redux";

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch();
  const incrementCounter = useCallback(
    () => dispatch({ type: "increment-counter" }),
    [dispatch]
  );

  return (
    <div>
      <span>{value}</span>
      <MyIncrementButton onIncrement={incrementCounter} />
    </div>
  );
};

export const MyIncrementButton = React.memo(({ onIncrement }) => (
  <button onClick={onIncrement}>Increment counter</button>
));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# useStore

back

const store = useStore();
//这个Hook返回redux <Provider>组件的store对象的引用。
1
2

这个钩子应该不常被使用。useSelector 应该作为你的首选。但是,有时候也很有用。来看个例子:

import React from "react";
import { useStore } from "react-redux";

export const CounterComponent = ({ value }) => {
  const store = useStore();

  // 仅仅是个例子! 不要在你的应用中这样做.
  // 如果store中的state改变,这个将不会自动更新
  return <div>{store.getState()}</div>;
};
1
2
3
4
5
6
7
8
9
10

# Hooks 配方

back

# useActions

这个是 alpha 的一个 hook,但是在 alpha.4 中听取 Dan 的建议被移除了。

# useShallowEqualSelector

import { shallowEqual } from "react-redux";

export function useShallowEqualSelector(selector) {
  return useSelector(selector, shallowEqual);
}
1
2
3
4
5