React.js - ForEach as a first-class component?

I heard about response patterns, but it still seemed to me that you could create a first-class ForEach component.

My ultimate goal is to make something like this more readable:

<ul> {list.map(function(item, i) { return <li>{item}</li>; })} </ul> // instead? <ul> <ForEach items="{list}"> <li>{item}</li> </ForEach> </ul> 

Here is my first serious attempt to skip the props:

 var ForEach = React.createClass({ render: function(){ return ( <ul> {this.props.items.map(function(item, i) { return React.Children.map(this.props.children, function(child) { return React.addons.cloneWithProps(child, {item: item}) }) }.bind(this))} </ul> ); } }); var Element = React.createClass({ render: function(){ return ( <li>{this.props.children}</li> ); } }); // usage within some other React.createClass render: <ForEach items={['foo', 'bar', 'baz']}> <Element>{this.props.item}</Element> </ForEach> 

The task I am facing is what this points out. Thanks to the one-step setup with the debugger, I see that I create cloned elements using this.props.item set, but since {this.props.item} is evaluated in the context of some other method of the rendering component, this not a cloned Element component - this is the ForEach parent.

{this.props.item} will work inside Element.render , but not where I want - I want to be able to pass Element some expression that interpolates the current element.

Perhaps this is not possible in React, or is there a way in which the ForEach component could reset state, like the current element / index, nested elements?

UPDATE I can significantly improve readability with ES6 arrow features. One set of curls goes with return (and possibly also a .bind(this) if you reference this inside a loop).

 <ul> {list.map((item, i) => <li>{item}</li> )} </ul> 

This is a long way to help with the syntactically awkward execution of the map line.

+6
source share
1 answer

My approach would be for ForEach expect the child of the function to receive a call for each element and simply insert a reaction element into the render.

It will be used something like this:

 render: function() { return ( <ForEach items={['foo', 'bar', 'baz']}> {function (item) { return ( <Element>{item}</Element> ) }/*.bind(this)*/} // optionally use this too </ForEach> ) } 

This would look better if you used ES6 Arrow Functions :

 render() { return ( <ForEach items={['foo', 'bar', 'baz']}> {(item) => // bind is not needed with arrow functions <Element>{item}</Element> } </ForEach> ) } 

Now, to implement ForEach :

 var ForEach = React.createClass({ getDefaultProps: function(){ return { element: 'ul', elementProps: {} }; }, render: function(){ return React.createElement( // Wrapper element tag this.props.element, // Optional props for wrap element this.props.elementProps, // Children this.props.items.map(this.props.children, this) ); } }); 

Pretty simple! The only caveat I discovered is that prop key needs to be set manually using the itterator function (possibly using key={index} )

Look at my basic example

+3
source

All Articles