首页 > 解决方案 > React/Redux/Reselect - mapStateToProps 即使在看到 reducer 中反映的更改后也会停止触发。

问题描述

我搜索了一个类似的问题,但找不到合适的帖子(......虽然我怀疑这个问题之前已经被问过)所以我提前道歉。

我现在已经在几个项目中使用 Redux 和 React,并且刚刚开始在学习项目中包含 immutable.js 和 Reselect。我的测试/学习项目只是将一个大型 JSON 对象加载到状态中,然后我使用 Reselect 来抓取它的一部分,然后传递到我的组件中。这是我正在加载的对象的示例:

export const formState = {
  progress: {
    groupId: 3,
    questionId: 5,
  },
  content: [
    {
      group: { id: 1, name: 'Company', description: 'Data regarding your company.', questions: 2 },
      questions: [
        {
          id: 1,
          type: 'textField',
          label: 'Required *',
          inputLabelProps: { shrink: true },
          text: 'group 1 question 1',
          answer: '',
        },
    ...
    ...
    ...
    ]
  }

该对象通过在 componentWillMount() 中触发的操作加载到状态,然后流经 reducer,最终触发组件内的 mapStateToProps,如下所示:

const mapStateToProps = (state) => {
   console.log(state);
   return {
     progress: getProgressData(state),
     content: getContentData(state),
   };
 };

这按预期工作。该组件有两个按钮,可让您遍历在上述“整体状态”的内容对象中找到的问题数组。

此外,当您按下前进或后退按钮遍历上述问题数组时,进度对象通过一个操作更新,然后通过以下方式在减速器中更新:

  switch (action.type) {
    case UPDATE_PROGRESS:
      formState = state.get('formState');
      formState.progress = action.payload;
      console.log(formState);
      return state
        .set('formState', formState);     

所以这就是奇怪的地方。我第一次向前或向后单击时,它可以工作,我看到对象得到更新,下一个问题显示在我的组件中。正如预期的那样......

如果我再次单击返回或下一步,我看到动作被触发,我看到减速器中反映了适当的状态,但我将不再在 mapStateToProps 的 console.log 中看到状态。它永远不会到那个地步。关于我做错了什么的任何想法?

注意:我不只是通过以下方式更新减速器中的状态可能看起来很奇怪: return state .setIn(['formState', 'progress'], formState)

我最初尝试这样做,但得到了无效的密钥路径。考虑到我正在通过 fromJS() 将初始状态转换为 immutable.js 映射,这对我来说很奇怪,所以我认为其中包含的任何内容都是映射或列表或任何合适的东西,因此可以通过这种方式寻址......无论如何,我想一旦我解决了上述问题,我就会解决这个问题。也就是说,除非它们是相关的。

与往常一样,如果这是一个重复的问题,我们将不胜感激并真诚地道歉。

谢谢!

标签: javascriptreactjsreduxreact-reduxreselect

解决方案


问题

Immutable.js 函数 Collection.get 返回给定键后面的值,在本例中是指向对象的指针。目前,您更改该对象,然后使用 Collection.set 将其设置回来。对象发生了变化,但指向该对象的指针(即存储在您的状态中的值)没有。因此,您的状态在技术上没有改变,并且您的组件不会更新。

出现此问题是因为您将普通对象存储在 Immutable.js 集合中。您应该只使用普通对象,或者只使用 Immutable.js 集合来安全地使用它。不要混合它们。

使固定

选项 1:Immutable.js 集合

将 state.formState 也设为 Immutable.js 集合。然后通过 Immutable.js 方法更改它。

switch (action.type) {
    case UPDATE_PROGRESS: {
      let formState = state.get('formState');
      formState = formState.set('progress', action.payload)
      console.log(formState);
      return state
        .set('formState', formState);
    }

如果你决定使用 Immutable.js,你应该确保不要留下任何 vanilla javascript 对象,因为它们可以绕过 Immutable.js 提供的保护。

选项 2:普通对象(推荐)

将 state 设为 vanilla javascript 对象。

switch (action.type) {
    case UPDATE_PROGRESS: {
      const formState = { ...state.formState };
      formState.progress = action.payload
      console.log(formState);
      return { ...state, formState };
    }

如果您决定不使用 Immutable.js,则必须小心不要意外直接更改状态。最简单的方法是使用扩展语法复制状态:const stateCopy = { ...state };


推荐阅读