2015-01-09 4 views
37

Я пытаюсь использовать ImmutableJS с моим приложением React/Flux.Когда использовать .toJS() с Immutable.js и Flux?

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

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

+1

Хороший вопрос. Я бы не делал этого в магазине, хотя, с тех пор вы теряете способность выполнять простое сравнение объектов (prevState! == this.state), если вы хотите оптимизировать рендеринг с shouldComponentUpdate. –

+0

Спасибо, действительно, это хороший момент, чтобы не использовать 'toJS()' в магазинах. – chollier

ответ

1

Хороший вопрос, поднятый @Hummlas.

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

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

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

42

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

Если в ваших магазинах 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> 
); 
} 
+4

Ли, есть ли у вас какие-либо советы для proptypes? Я привык использовать 'arrayOf (shape ...)'. – Zach

+0

теперь, если поддерживаются только мои внешние библиотеки. Immutable.js – pherris

+4

Итак, говорим ли мы, что использование read API ('get()', 'getIn()') из Immutable.js в компонентах React - это правильная вещь? Это похоже на пропущенную абстракцию и означает, что если я когда-либо изменю способ сохранения состояния в своих магазинах, я должен коснуться каждого компонента, который читает значение. Что-то не так в этом поводу ... – sethro

0

- больше не рекомендуется -
При использовании Redux я, как правило, пусть mapStateToProps моих Подключается Функция преобразования неизменных структур с использованием toJS() и позволяют моим компонентам реагировать на использование реквизита в качестве объектов javascript.

+3

Но toJS вернет новый объект deepclone. Это не хорошо для исполнения. –

+0

Это правда, но, используя что-то вроде повторного выбора, вы можете вернуть новый объект только в том случае, если изменился основной неизменяемый. Единственная проблема заключается в вложенных данных. Например, если изменился только один элемент списка карт, будет возвращен целый новый массив с новыми объектами, запускающий рендер в каждом дочернем представлении. С неизменяемым только ребёнок, который сделал изменения, будет повторно представлен ... – Ingro

+3

Я прекратил делать то, что я предложил из-за производительности. – walkerrandophsmith

3

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

Представьте иметь магазин как это:

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 в качестве опоры, которая будет отображенный в 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, поэтому, как объекты возвращаются из магазина, больше не занимается компонентами.

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

+0

замечательное решение! –

3

Как @LeeByron сказал, вы не должны вызывать toJS. Под Реагировать 0.14 *, называя map на непреложный Map будет работать и правильно делают, но вы будете в конечном вверх с предупреждением:.

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

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

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

Преобразование вашей итерации в массив и давая React, что он хочет.

+0

Спасибо, что ответ так долго смотрел! – user2861799

+0

@ user2861799 Добро пожаловать, рад, что это помогло :) –