草庐IT

javascript - 更新深层不可变状态属性时,Redux 不更新组件

coder 2024-07-24 原文

我的问题:为什么在我的不可变状态(映射)中更新数组中对象的属性不会导致 Redux 更新我的组件?

我正在尝试创建一个将文件上传到我的服务器的小部件,我的初始状态(来 self 的 UploaderReducer 内部,您将在下面看到)对象如下所示:

let initState = Map({
  files: List(),
  displayMode: 'grid',
  currentRequests: List()
});

我有一个 thunk 方法,它在事件发生时(例如进度更新)开始上传和分派(dispatch)操作。例如,onProgress 事件如下所示:

onProgress: (data) => {
    dispatch(fileUploadProgressUpdated({
      index,
      progress: data.percentage
    }));
  } 

我正在使用 redux-actions创建和处理我的 Action ,所以我的那个 Action 的 reducer 看起来像这样:

export default UploaderReducer = handleActions({
  // Other actions...
  FILE_UPLOAD_PROGRESS_UPDATED: (state, { payload }) => (
    updateFilePropsAtIndex(
      state,
      payload.index,
      {
        status: FILE_UPLOAD_PROGRESS_UPDATED,
        progress: payload.progress
      }
    )
  )
  }, initState);

updateFilePropsAtIndex看起来像:

export function updateFilePropsAtIndex (state, index, fileProps) {
  return state.updateIn(['files', index], file => {
    try {
      for (let prop in fileProps) {
        if (fileProps.hasOwnProperty(prop)) {
          if (Map.isMap(file)) {
            file = file.set(prop, fileProps[prop]);
          } else {
            file[prop] = fileProps[prop];
          }
        }
      }
    } catch (e) {
      console.error(e);
      return file;
    }

    return file;
  });
}

到目前为止,这一切似乎工作正常!在 Redux DevTools 中,它显示为预期的操作。但是,我的组件都没有更新!将新项目添加到 files array 使用添加的新文件重新呈现我的 UI,所以 Redux 对我这样做肯定没有问题......

我的顶级组件使用 connect 连接到商店看起来像这样:

const mapStateToProps = function (state) {
  let uploadReducer = state.get('UploaderReducer');
  let props = {
    files: uploadReducer.get('files'),
    displayMode: uploadReducer.get('displayMode'),
    uploadsInProgress: uploadReducer.get('currentRequests').size > 0
  };

  return props;
};

class UploaderContainer extends Component {
  constructor (props, context) {
    super(props, context);
    // Constructor things!
  }

  // Some events n stuff...

  render(){
      return (
      <div>
        <UploadWidget
          //other props
          files={this.props.files} />
       </div>
       );
  }
}

export default connect(mapStateToProps, uploadActions)(UploaderContainer);  

uploadActions是一个具有使用 redux-actions 创建的操作的对象.

A file files 中的对象数组基本上是这样的:

{
    name: '',
    progress: 0,
    status
}

UploadWidget基本上是一个拖放 div 和一个 files数组打印在屏幕上。

我尝试使用 redux-immutablejs正如我在 GitHub 上的许多帖子中看到的那样提供帮助,但我不知道它是否有帮助......这是我的根 reducer :

import { combineReducers } from 'redux-immutablejs';
import { routeReducer as router } from 'redux-simple-router';
import UploaderReducer from './modules/UploaderReducer';

export default combineReducers({
  UploaderReducer,
  router
});

我的应用程序入口点如下所示:

const store = configureStore(Map({}));

syncReduxAndRouter(history, store, (state) => {
  return state.get('router');
});

// Render the React application to the DOM
ReactDOM.render(
  <Root history={history} routes={routes} store={store}/>,
  document.getElementById('root')
);

最后,我的 <Root/>组件看起来像这样:

import React, { PropTypes } from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router';

export default class Root extends React.Component {
  static propTypes = {
    history: PropTypes.object.isRequired,
    routes: PropTypes.element.isRequired,
    store: PropTypes.object.isRequired
  };

  get content () {
    return (
      <Router history={this.props.history}>
        {this.props.routes}
      </Router>
    );
  }

//Prep devTools, etc...

  render () {
    return (
      <Provider store={this.props.store}>
        <div style={{ height: '100%' }}>
          {this.content}
          {this.devTools}
        </div>
      </Provider>
    );
  }
}

因此,最终,如果我尝试更新以下状态对象中的“进度”,React/Redux 不会更新我的组件:

 {
    UploaderReducer: {
        files: [{progress: 0}]
    }
 }

这是为什么?我认为使用 Immutable.js 的整个想法是无论更新对象有多深,都可以更轻松地比较修改后的对象?

似乎通常让 Immutable 与 Redux 一起工作并不像看起来那么简单: How to use Immutable.js with redux? https://github.com/reactjs/redux/issues/548

然而,使用 Immutable 的吹捧好处似乎值得这场战斗,我很想弄清楚我做错了什么!

2016 年 4 月 10 日更新 选定的答案告诉我我做错了什么,为了完整起见,我的 updateFilePropsAtIndex函数现在只包含这个:

return state.updateIn(['files', index], file =>
  Object.assign({}, file, fileProps)
);

这非常有效! :)

最佳答案

首先有两个一般的想法:

  • Immutable.js 是潜在有用的,是的,但是您可以在不使用它的情况下完成相同的不可变数据处理。有许多库可以帮助使不可变数据更新更易于阅读,但仍然对普通对象和数组进行操作。我在 Immutable Data 上列出了很多我的 Redux 相关库存储库中的页面。
  • 如果 React 组件似乎没有更新,那几乎总是因为 reducer 实际上在改变数据。 Redux 常见问题解答在 http://redux.js.org/docs/FAQ.html#react-not-rerendering 处有关于该主题的答案。 .

现在,鉴于您正在使用 Immutable.js,我承认数据突变似乎不太可能。那就是说...您的 reducer 中的 file[prop] = fileProps[prop] 行看起来确实非常奇怪。你到底期望在那里发生什么?我会好好看看那部分。

实际上,现在我看了一下......我几乎 100% 确定你正在改变数据。您对 state.updateIn(['files', index]) 的更新程序回调返回与您作为参数获得的完全相同的文件对象。根据位于 https://facebook.github.io/immutable-js/docs/#/Map 的 Immutable.js 文档:

If the updater function returns the same value it was called with, then no change will occur. This is still true if notSetValue is provided.

是的。你返回的是你被赋予的相同值,你对它的直接突变会出现在 DevTools 中,因为该对象仍然存在,但由于你返回了相同的对象,Immutable.js 实际上并没有返回任何修改过的对象层次结构。因此,当 Redux 检查顶级对象时,它发现没有任何变化,不会通知订阅者,因此您的组件的 mapStateToProps 永远不会运行。

清理你的 reducer 并从更新器内部返回一个新对象,它应该一切正常。

(一个相当迟来的答案,但我刚刚看到这个问题,它似乎仍然是开放的。希望你现在真的已经解决了......)

关于javascript - 更新深层不可变状态属性时,Redux 不更新组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35676952/

有关javascript - 更新深层不可变状态属性时,Redux 不更新组件的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  5. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  6. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  7. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  8. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  9. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  10. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

随机推荐