Redux не обновляет компоненты при обновлении свойств глубокого неизменяемого состояния

мой вопрос: почему обновление свойства объекта в массиве в моем неизменяемом состоянии (Map) не заставляет Redux обновлять мой компонент?

я пытаюсь создать виджет, который загружает файлы на мой сервер, и мое начальное состояние (изнутри моего UploaderReducer, который вы увидите ниже) объект выглядит так:

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

у меня есть метод thunk, который запускает загрузку и отправляет действия при возникновении события (например, обновление прогресса). Например, событие onProgress выглядит следующим образом:

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

я использую redux-actions для создания и обработки моих действий, так что мой редуктор для этого действия выглядит так:

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 массив повторно отображает мой пользовательский интерфейс с добавлением новых файлов, поэтому 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

1 ответов


сначала две общие мысли:

  • незыблемыми.js is потенциально полезно, да, но вы можете выполнить ту же неизменяемую обработку данных без ее использования. Существует ряд библиотек, которые могут облегчить чтение неизменяемых обновлений данных, но по-прежнему работают с обычными объектами и массивами. У меня многие из них перечислены на Неизменяемые Данные страница в моем репозитории библиотек, связанных с Redux.
  • если компонент React не по-видимому, обновление, это почти всегда потому, что редуктор фактически мутирует данные. В FAQ Redux есть ответ на эту тему, вhttp://redux.js.org/docs/FAQ.html#react-not-rerendering.

Теперь, учитывая, что вы are использовать immutable.js, я признаю, что мутация данных кажется немного маловероятной. Вот и все... the file[prop] = fileProps[prop] линия в вашем редукторе кажется ужасно любопытной. Что именно вы собираетесь там делать? Я бы получше посмотри на эту часть.

вообще-то, теперь, когда я смотрю на это... Я почти на 100% уверен, что вы мутируете данные. Ваш обратный вызов updater в state.updateIn(['files', index]) возвращает тот же объект файл, который вы получили в качестве параметра. За неизменное.JS docs at https://facebook.github.io/immutable-js/docs/#/Map:

Если функция updater возвращает то же значение, с которым она была вызвана, то никаких изменений не произойдет. Это по-прежнему верно, если notSetValue предоставлена.

Да. Вы возвращаете то же значение, которое вам было дано, ваши прямые мутации к нему появляются в DevTools, потому что этот объект все еще висит вокруг, но так как вы вернули тот же объект неизменяемым.js фактически не возвращает никаких измененных объектов дальше по иерархии. Таким образом, когда Redux выполняет проверку объекта верхнего уровня, он видит, что ничего не изменилось, не уведомляет подписчиков, и поэтому ваш компонент mapStateToProps никогда работает.

очистите свой редуктор и верните новый объект изнутри этого обновления, и это должны все просто получается.

(довольно запоздалый ответ, но я только что видел вопрос, и он, кажется, все еще открыт. Надеюсь, ты его уже починил...)