web-dev-qa-db-ja.com

Immutable.jsおよびFluxで.toJS()を使用する場合

React/FluxアプリケーションでImmutableJSを使用しようとしています。

私のストアは_Immutable.Map_オブジェクトです。

どの時点で.toJS()を使用すべきか疑問に思っていますか?ストアの.get(id)が戻るときですか?または.get('member')のあるコンポーネントで?

41
chollier

理想的には、決して!

FluxストアがImmutable.jsを使用している場合は、ずっと維持するようにしてください。 React.addons.ReactComponentWithPureRenderMixinを使用して、メモ化のパフォーマンスを向上させます(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.で変更されました。コンポーネントはArrayだけではなく子としてIterableを受け入れます。Immutable.jsはIterableを実装するため、toJS()

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

ちょっと古い質問ですが、最近、 reselectlodash's memoize を使用してこのアプローチを試し、Reactのコンポーネントに同等のオブジェクトを返す努力をしています。

次のような店があると想像してください。

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

    // ...
}
_

このように、todoのストアで何も変更がなければ、getTodos() reselectを呼び出すと同じオブジェクトが返されるため、何も再レンダリングされません。

たとえば、IDが_2_のtodoが完了とマークされている場合、ストアでも変更されるため、todosSelectorによって新しいオブジェクトが返されます。次に、todoはmemoizeTodo関数によってマップされます。この関数は、todoが変更されない場合は同じオブジェクトを返す必要があります(不変マップであるため)。したがって、TodoListは新しい小道具を受け取ると、todosが変更されたため再レンダリングされますが、id 1のtodoを表すオブジェクトが変更されたため、2番目のTodoItemのみが再レンダリングされます変わらない。

これは、特にストアに多くのアイテムが含まれている場合にパフォーマンスの低下につながる可能性がありますが、中規模アプリでは問題に気付きませんでした。このアプローチの利点は、コンポーネントが小道具としてプレーンなJavaScriptオブジェクトを受け取り、PureRenderMixinのようなものでそれらを使用できるため、ストアからオブジェクトが返される方法はコンポーネントのビジネスではなくなります。

4
Ingro

@LeeByronが言ったように、toJSを呼び出す必要はありません。 React 0.14。*​​)で、不変のmapMapを呼び出すと機能し、正しくレンダリングされますが、警告が表示されます:

Mapsを子として使用することはまだ完全にはサポートされていません。これは実験的な機能であり、削除される可能性があります。代わりに、キー付きReactElementsのシーケンス/反復可能に変換します。

これに対処するには、MaptoArray()を次のように呼び出すことができます。

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

イテラブルを配列に変換し、React欲しいものを与える。

3
Balthazar

@Hummlasによる良い点。

サブコンポーネントの配列をレンダリングするためにコレクションを反復処理するときに、Reactコンポーネントで個人的に使用します:

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

.toJS()を使用しない場合、Reactはマップされた要素をコンポーネントの配列として認識しません。

2
Joseph

-推奨されなくなりました-
[。

0