Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react-redux 的 hooks #24

Open
brunoyang opened this issue Jul 2, 2019 · 1 comment
Open

react-redux 的 hooks #24

brunoyang opened this issue Jul 2, 2019 · 1 comment

Comments

@brunoyang
Copy link
Owner

brunoyang commented Jul 2, 2019

在v7.1中,react-redux加上了hooks,接下来我们就来看看新hooks,以及需要如何修改组件。
如果你还不清楚什么是hooks,建议阅读此文档

hooks

  • useSelector,接受传入一个函数,此函数的入参是store,可以理解为原来的mapStateToProps函数;
  • useDispatch,没有参数,返回值就是dispatch,可以理解为原来的mapDispatchToProps;
  • useStore,获得store,但尽量不要用这个方法,因为获得的store不是响应式的,只是一个快照。

useSelector

签名:

const result : any = useSelector(selector : Function, equalityFn? : Function)

举例来说:

import { shallowEqual, useSelector } from 'react-redux'
const count = useSelector(stoer => store.count, shallowEqual); // 第二个参数可选,也可以换成自定义的比较函数

useDispatch

用法很简单

const dispatch = useDispatch;
dispatch({ type: 'action1' })

使用connect的写法

import React from 'react';
import { connect } from 'react-redux';

function componentWithConnect({ count, addCount }) {
  return (
    <>
      <span>{count}</span>
      <button onClick={addCount}>点击</button>
    </>
  );
}

const mapStateToProps = (store) => ({
  count: store.count,
});
const mapDispatch = (dispatch) => ({
  addCount: () => dispatch({ type: 'addCount' }),
});
export default connect(mapStateToProps, mapDispatch)(componentWithConnect);

换成hooks的写法

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

export default function componentWithHooks() {
  const { count } = useSelector(store => ({
    count: store.count,
  }));
  const dispatch = useDispatch();
  const addCount = () => dispatch({ type: 'addCount' });

  return (
    <>
      <span>{count}</span>
      <button onClick={addCount}>点击</button>
    </>
  );
}

在新组件中,我们去掉了connect,使用了useSelectoruseDispatch两个hooks。这样就不用再从props传入,让逻辑更为内聚。(另外还有一个好处就是在React Devtools里也少了一层嵌套)

消失的useActions?

可能有的同学会有疑问,useDispatch并没有完全代替mapDispatchToProps,为什么不加上useActions呢?
实际上在beta版本中,react-redux是带有useActions的,但在dan的建议下,去掉了这个api。为什么呢?其实只要自己模拟一下,你就能知道为什么了。

function someComponet() {
  // !!!!没有 useActions 这个api!!!!
  const actions = useActions((dispatch) => ({
    action1: () => dispatch({ type: 'action1' }),
    action2: () => dispatch({ type: 'action2' }),
  }));

  useEffect(() => {
    actions.action1();
    actions.action2();
  }, [actions.action1, actions.action2]);
}

看看为了发送一个dispatch,写了多少模板代码,要是有更多的action,useEffect的依赖列表就会变得更长。此外,在得到actions后,几乎是立即就又被解包了,在使用connect的情况下可能还是可以接受的,但在使用了hooks后,无疑是画蛇添足。
其实在过去,通过mapDispatchToProps() => dispatch({ type: 'action1' })包装成一个actionCreator,是不是有过分迷恋这种『一行缩写』的嫌疑呢?更不要说这种缩写会让阅读代码的人搞不清数据流。
我们再来看如果直接使用useDispatch是怎么样的

function someComponet() {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch({ type: 'action1' });
    dispatch({ type: 'action2' });
  }, [dispatch]);
}

是不是更简单,也更清晰了呢。

自定义hooks

在很多情况下,useDispatch已经够用,但在一些复杂情况下,还是需要自定义hooks的。如要在多个组件间共享逻辑,自定义hooks是个不错的选择。

// complexActions.js
const complexActions = () => {
  const dispatch = useDispatch();
  dispatch({ type: 'actions1' });
  dispatch({ type: 'actions2' });
  someRequest().then(() => {
  	dispatch({ type: 'actions3' });	
  });
  // balbala...
};

// componetA.js
function componentA() {
  useEffect(() => {
  	complexActions();
  }, [complexActions]);
  
  return (...);
}

// componetB.js
function componentB() {
  useEffect(() => {
  	complexActions();
  }, [complexActions]);
  
  return (...);
}

性能优化建议

触发dispatch后,useSelector被触发(因为useSelector是使用useEffect实现,useEffect的依赖就有store),当发现前后两次state不一样时,会触发重渲染。此时的问题是当不相关的值被修改,本组件仍然会重渲染。可以使用reselect来减少这种情况,具体怎么做,可以参考官方文档
另外,connect也会对props做一次浅比较,防止重渲染。所以去掉connect之后,就需要使用React.memo去做了。

import React from 'react';
function componentA(props) { return <>...</> }
export default React.memo(componentA);

参考

@crazyair
Copy link

—_—

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants