我应该使用 useselector/useDispatch 而不是 mapStateToProps

新手上路,请多包涵

在创建 React 应用程序时,如果我使用钩子 useSelector ,我需要遵守钩子调用规则(只能从功能组件的顶层调用它)。如果我使用 mapStateToProps ,我得到了道具中的状态,我可以在任何地方使用它而没有任何问题……同样的问题 useDispatch

mapStateToProps 相比,除了节省代码行之外,使用钩子还有什么好处?

原文由 Yonatan Nir 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 839
2 个回答

可以从组件中的任何位置读取和更改 Redux 存储状态,包括回调。每当商店状态更改时,组件都会重新呈现。当组件重新呈现时,useSelector 再次运行,并为您提供更新后的数据,稍后可以在您想要的任何地方使用。这是一个例子,以及 useDispatch 在回调中的用法(在根级别赋值之后):

 function Modal({ children }) {
  const isOpen = useSelector(state => state.isOpen);
  const dispatch = useDispatch();
  function handleModalToggeled() {
    // using updated data from store state in a callback
    if(isOpen) {
      // writing to state, leading to a rerender
      dispatch({type: "CLOSE_MODAL"});
      return;
    }
    // writing to state, leading to a rerender
    dispatch({type: "OPEN_MODAL"});
  }
  // using updated data from store state in render
  return (isOpen ? (
      <div>
        {children}
        <button onClick={handleModalToggeled}>close modal</button>
      </div>
    ) : (
      <button onClick={handleModalToggeled}>open modal</button>
    );
  );
}

使用 useSelector 和 useDispatch 钩子不能用 mapStateToProps/mapDispatchToProps 做的任何事情。

话虽如此,这两种方法之间存在一些值得考虑的差异:

  1. 解耦:使用 mapStateToProps ,容器逻辑(将存储数据注入组件的方式)与视图逻辑(组件渲染)分离。 useSelector 代表了一种新的和不同的连接组件的思考方式,认为组件之间的解耦更为重要并且组件是自包含的。哪个更好?结论:没有明确的赢家。 资源
  2. DX(开发人员经验):使用 connect 函数通常意味着每个连接的组件应该有另一个额外的容器组件,其中使用 useSelector 和 useDispatch 挂钩非常简单。结论:钩子有更好的 DX。
  3. “Stale props”和“Zombie child”:useSelector 有一些奇怪的边缘情况,如果它依赖于 props,其中 useSelector 可以在最新更新的 props 进来之前运行。这些大多是罕见且可以避免的边缘案例,但它们已经在较旧的 connect 版本中得到解决。结论:connect 比 hooks 稍微稳定一些。 资源
  4. 性能优化:两者都以不同的方式支持性能优化: connect 有一些高级技术,使用隐藏在连接函数中的合并道具和其他选项。 useSelector 接受第二个参数——一个判断状态是否改变的相等函数。结论:两者都非常适合高级情况下的性能。
  5. 类型:使用打字稿 connect 是一场噩梦。我记得自己狂热地为每个连接的组件编写了三个道具接口(OwnProps、StateProps、DispatchProps)。 Redux hooks 以一种相当直接的方式支持类型。结论:类型使用钩子更容易处理。
  6. React 的未来:Hooks 是 React 的未来。这可能看起来像一个奇怪的论点,但“并发模式”和“服务器组件”对生态系统的改变指日可待。虽然在未来的 React 版本中仍将支持类组件,但新功能可能仅依赖于 hooks。这种变化当然也会影响生态系统中的第三方库,例如 React-Redux。结论:钩子更适合未来。

TL;DR - 最终裁决:每种方法都有其优点。 connect 更成熟,出现奇怪错误和边缘情况的可能性更小,并且具有更好的关注点分离。 Hooks 更易于阅读和编写,因为它们位于使用它们的地方附近(全部在一个独立的组件中)。此外,它们更易于与 TypeScript 一起使用。最后,它们可以很容易地升级到未来的 React 版本。

原文由 deckele 发布,翻译遵循 CC BY-SA 4.0 许可协议

我认为您误解了“顶级”是什么。它仅仅意味着,在功能组件内部, useSelector() 不能放置在循环、条件和嵌套函数内。它与根组件或组件结构没有任何关系

// bad
const MyComponent = () => {
  if (condition) {
    // can't do this
    const data = useSelector(mySelector);
    console.log(data);
  }

  return null;
}

---

// good
const MyComponent = () => {
  const data = useSelector(mySelector);

  if (condition) {
    console.log(data); // using data in condition
  }

  return null;
}

如果有的话, mapStateToPtops 位于比挂钩调用更高的级别

钩子的规则使得使用那个特定的钩子变得非常困难。您仍然需要以某种方式从回调中的状态访问不断变化的值

公平地说,您几乎不必在回调中访问不断变化的值。我不记得上次我需要那个了。通常,如果你的回调需要最新状态,你最好只分派一个动作,然后该动作的处理程序(redux-thunk、redux-saga、redux-observable 等)本身将访问最新状态

这只是一般钩子的细节(不仅仅是 useSelector),如果你真的想要的话,有很多方法可以绕过它,例如

const MyComponent = () => {
  const data = useSelector(mySelector);
  const latestData = useRef()
  latestData.current = data

  return (
    <button
      onClick={() => {
        setTimeout(() => {
          console.log(latestData.current) // always refers to latest data
        }, 5000)
      }}
    />
  )
}

与 mapStateToProps 相比,使用 hook 除了节省代码行之外还有什么好处?

  1. 通过在需要访问存储时不编写连接函数并在不再需要访问存储时删除它来节省时间。 React devtools 中没有无尽的包装器
  2. 来自连接的道具、来自父级的道具和来自第三方库的包装器注入的道具之间有明显的区别并且没有冲突
  3. 有时您(或与您一起工作的开发人员)会在 mapStateToProps 中为道具选择不明确的名称,您将不得不一直滚动到文件中的 mapStateToProps 以找出哪个选择器是用于这个特定的道具。这不是钩子的情况,选择器和带有它们返回的数据的变量耦合在同一行上
  4. 通过使用钩子,您可以获得钩子的一般优势,其中最大的优势是能够耦合在一起并在多个组件中重用相关的有状态逻辑
  5. 使用 mapStateToProps 你通常必须处理 mapDispatchToProps 这更麻烦,更容易迷路,尤其是阅读别人的代码(对象形式?函数形式?bindActionCreators?)。来自 mapDispatchToProps 的道具可以与其动作创建者具有相同的名称但不同的签名,因为它在 mapDispatchToprops 中被覆盖。如果您在多个组件中使用一个动作创建器,然后重命名该动作创建器,这些组件将继续使用来自道具的旧名称。如果你有一个依赖循环并且你必须处理阴影变量名称,那么对象形式很容易破坏

.

 import { getUsers } from 'actions/user'

class MyComponent extends Component {
  render() {
    // shadowed variable getUsers, now you either rename it
    // or call it like this.props.getUsers
    // or change import to asterisk, and neither option is good
    const { getUsers } = this.props
    // ...
  }
}

const mapDispatchToProps = {
  getUsers,
}

export default connect(null, mapDispatchToProps)(MyComponent)

原文由 Max 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题