Skip to content
New issue

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

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

Already on GitHub? # 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

—_—

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

No branches or pull requests

2 participants