Подтвердить что ты не робот

Когда использовать .toJS() с Immutable.js и Flux?

Я пытаюсь использовать ImmutableJS с моим приложением React/Flux.

Мои магазины - это объекты Immutable.Map.

Мне интересно, в какой момент следует использовать .toJS()? Должно ли быть, когда магазин .get(id) возвращается? или в компонентах с .get('member')?

4b9b3361

Ответ 1

В идеале никогда!

Если ваши хранилища Flux используют Immutable.js, попробуйте продолжить весь путь. Используйте React.addons.ReactComponentWithPureRenderMixin для достижения выигрыша производительности memoization (он добавляет методы shouldComponentUpdate).

При рендеринге вам может потребоваться вызвать toJS(), так как React v0.12.x принимает только Array как дети:

render: function () {
  return (
    <div>
      {this.props.myImmutable.map(function (item) {
        <div>{item.title}</div>
      }).toJS()}
    </div>
  );
}

Это изменилось в React v0.13.x. Компоненты принимают любые Iterable как дети, а не только Array. Поскольку Immutable.js реализует Iterable, вы можете опустить toJS():

render: function () {
  return (
    <div>
      {this.props.myImmutable.map(function (item) {
        <div>{item.title}</div>
      })}
    </div>
  );
}

Ответ 2

Вопрос о давности, но в последнее время я экспериментирую с этим подходом, используя reselect и lodash memoize в попытке вернуть сопоставимые объекты в React Components.

Представьте, что у вас есть такой магазин:

import { List, Map } from 'immutable';
import { createSelector } from 'reselect';
import _ from 'lodash'; 

const store = {
    todos: List.of(
        Map({ id: 1, text: 'wake up', completed: false }), 
        Map({ id: 2, text: 'breakfast', completed: false })
    )
};

const todosSelector = state => state.todos;

function normalizeTodo(todo) {
    // ... do someting with todo
    return todo.toJS();
}

const memoizeTodo = _.memoize(normalizeTodo);

export const getTodos = createSelector(
    todosSelector,
    todos => todos.map(memoizeTodo)
);

Затем перейдем к компоненте TodoList todos как prop, который будет отображаться в 2 TodoItem Компоненты:

class TodoList extends React.Component {
    shouldComponentUpdate(nextProps) {
         return this.props.todos !== nextProps.todos;
    }

    render() {
       return (<div>
           {this.props.todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
       </div>);
    }
}

class TodoItem extends React.Component {
    shouldComponentUpdate(nextProps) {
        return this.props.todo !== nextProps.todo;
    }

    // ...
}

Таким образом, если ничего не изменилось в хранилище todos, когда я вызываю getTodos() reselect возвращает мне один и тот же объект, и поэтому ничего не получается повторно.

Если, например, todo с id 2 si, отмеченным как завершенным, оно также изменяется в хранилище, и поэтому новый объект возвращается todosSelector. Затем todos отображаются функцией memoizeTodo, которая должна возвращать тот же объект, если todo не изменяется (поскольку они являются неизменяемыми). Поэтому, когда TodoList получает новые реквизиты, он повторно рендерится, потому что todos изменился, но только второй TodoItem повторный рендер, потому что объект, представляющий todo с id 1, не изменился.

Это, безусловно, может привести к потере производительности, особенно если наш магазин содержит много предметов, но я не заметил никаких проблем в своем приложении среднего размера. Поверхность этого подхода заключается в том, что ваши компоненты получают простые javascript-объекты в качестве реквизита и могут использовать их с чем-то вроде PureRenderMixin, поэтому как объекты возвращаются из хранилища, больше не являются компонентом Компонентов.

Надеюсь, это имеет смысл, мой английский очень плохой:/

Ответ 3

Как и @LeeByron, вам не нужно называть toJS. В разделе React 0.14. * Вызов map на неизменяемом map будет работать и отображать правильно, но вы получите предупреждение:

Использование карт в качестве детей еще не полностью поддерживается. Это экспериментальная функция, которая может быть удалена. Преобразуйте его в последовательность/итерабельность клавишных ReactElements.

Чтобы справиться с этим, вы можете вызвать toArray() на свой map, например:

render () {
  return (
    <div>
      {this.props.immutableMap.toArray().map(item => {
        <div>{item.title}</div>
      })}
    </div>
  )
}

Преобразование вашего итерабельного в массив и предоставление Реагировать то, что он хочет.

Ответ 4

Хорошая точка, поднятая @Hummlas.

Я лично использую его в своих компонентах React, когда я перебираю коллекции, чтобы отобразить массив подкомпонентов:

this.props.myImmutableCollection.map(function(item, index) {
    React.DOM.div null, item.get('title');
}).toJS();

Если вы не используете .toJS(), React не распознает отображенные элементы как массив компонентов.

Ответ 5

- Больше не рекомендую -
При использовании Redux я стараюсь, чтобы функция myStationCountStateToProps меняла неизменяемые структуры с помощью функции jS() и позволяла моим компонентам реагировать на использование реквизита в качестве объектов javascript.