React/Redux/Reselect - mapStateToProps 即使在看到 reducer 中反映的更改后也会停止触发。

React/Redux/Reselect - mapStateToProps stops firing even after seeing changes reflected in the reducer.

我搜索了类似的问题,但找不到合适的问题 post(...虽然我怀疑这个问题之前有人问过)所以我提前道歉。

我现在已经在几个项目中使用 Redux 和 React,并且刚刚开始在一个学习项目中包括 immutable.js 和 Reselect。我的 testing/learning 项目只是将一个大的 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),
   };
 };

这按预期工作。该组件有两个按钮,可让您遍历在上述 "overall state."

的内容对象中找到的问题数组

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

  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 状态 .setIn(['formState', 'progress'], formState)

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

一如既往,如果这是一个重复的问题,我们将不胜感激,我们深表歉意。

谢谢!

你不应该在 reducer 中更新状态。它几乎总是会导致奇怪的行为,immutable-update-patterns。您是否尝试返回新对象?即

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

问题

Immutable.js 函数 Collection.get returns 给定键后面的值,在本例中是指向 object 的指针。目前,您可以更改 object,然后使用 Collection.set 将其重新设置。 object 发生了变化,但是指向 object 的指针(即存储在您的状态中的值)没有发生变化。因此,您的状态在技术上没有变化,您的组件也没有更新。

出现此问题是因为您将纯 object 存储在 Immutable.js Collection 中。您应该只使用普通的 objects 或只使用 Immutable.js Collections 以确保安全。不要混用。

修复

选项 1:Immutable.jsCollections

也使state.formState成为Immutable.jsCollection。然后通过 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,您应该确保不要留在任何原版 javascript object 中,因为它们可以绕过 Immutable.js 提供的保护.

选项 2:普通 objects(推荐)

将状态设为原版 javascript object.

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 };