First component
Let’s start from a component that allow the input of multiple publications. So there should always be 1 empty form for a publication, and the user can click on something to add another. Then the user should also be able to remove them as wished.
class AppWidget extends Component { constructor(props) { super(props); this.handle_click = props.handle_click.bind(this); } render() { return ( <fieldset> <legend>Publications</legend> {this.props.publications.map((item, idx) => ( <Publication key={idx} publication={item} delegate_update={this.props.handle_update.bind(this, idx)} /> ))} <a onClick={this.handle_click} href="#"> Add new publiations </a> </fieldset> ); } }
(I am going to show only the render function in the following examples)
So the code above is very much only for presentation purpose, nothing much to talk about. It lists all the available publications. Then there’s also a link to click so a new publication form is added to the list.
Then we have the respective container created through react-redux connect
function.
export default connect( state => ({ publications: state.publications || [{}] }), dispatch => ({ handle_click(e) { e.preventDefault(); dispatch(make_publication_update(this.props.publications.concat([{}]))); }, handle_update(position, is_delete, changes) { return make_publication_update( is_delete ? this.props.publications.filter((_, idx) => idx !== position) : this.props.publications.map( (_current, idx) => idx === position ? Object.assign({}, _current, changes) : _current ) ); } }) )(AppWidget);
Remember in the previous page I mentioned I only call dispatch in the browser event handlers. In this case I am concatenating a new empty publication to the list, send it to the action creator, and dispatch the action.
The more interesting part is the handle_update
function here. It is of the second form, which has a position required in the function parameters. I could have passed the position as a property to the component, however I decided in the end not to because the component doesn’t have to know its position to serve its purpose, plus this would allow it to be used elsewhere when it is not in a list. This is why I bound the position in before sending it in as delegate_update
property.
This also indirectly makes all delegate_update
property share the same function call signature, which is delegate_update(is_delete, changes)
The parameter is_delete
is placed in the first because whenever a delete operations is needed it would work with only 1 argument passed in. We shall see it in action in the following Publication
component.