Kyle Truong Kyle Truong - 2 months ago 46
React JSX Question

How do you create multiple forms on the same page with redux-forms v6?

I have a simple todo app in which my redux store contains an array of 'todos'. My 'Todo' component maps over every 'todo' in the store and renders a 'TodoForm' component that uses redux-forms v6.

As it is now, every 'todo' shares the same form name/key, so every time I input something in the 'title' Field, it changes the 'title' of every todo. I found a work around by using unique Field names, but I fear it's going to over complicate things as the app grows, and would prefer to use unique Form names so every field can have the same name without interfering with the other forms

(TodoForm1, TodoForm2, TodoForm3 can all have a unique 'title' Field instead of TodoForm containing 'title1', 'title2', 'title3' Fields).

I tried accessing the TodoForm's props so I could set each form's key as the component's unique id, but it doesn't seem like the component receives props that early.

I also tried making an immediately invoked function where it spits out a random number, and using that number as the form's name, but that also didn't work.

How can I can map through all my todos and render a v6 redux-form with a unique form key?

Here's a picture of the app, console, and redux devtools. There's 3 'todos', but there's only one form that connects them all, todo-926, even though each form key should have been randomly generated in an immediately invoked function:

Todo Conundrums

HomePageMainSection.index.js

renderTodos(todo) {
if (!todo) {
return <div>No Todos</div>;
}
return (
<div key={todo.get('id')}>
<Todo
todo={todo}
updateTodo={this.props.updateTodo}
deleteTodo={this.props.deleteTodo}
/>
</div>
);
}

render() {
if (!this.props.todos) {
return <div>No Todos</div>;
}

return (
<div className={styles.homePageMainSection}>
<h1>Hey I'm the Main Section</h1>
<div>
{this.props.todos.get('todos').map(this.renderTodos)}
</div>
</div>
);
}


Todo.index.js:

renderTodo() {
if (this.state.editMode) {
return (
<TodoForm
todo={this.props.todo} changeTodoEditMode={this.changeTodoEditMode}
updateTodo={this.props.updateTodo}
/>
);
}

return (
<div className={styles.Todo} onClick={this.changeTodoEditMode}>
<div className="card card-block">
<h4 className="card-title">{this.props.todo.get('author')}</h4>
<p className="card-text">{this.props.todo.get('title')}</p>
<i
className={`${styles.deleteIcon} btn btn-danger fa fa-times`}
onClick={this.deleteTodo}
></i>
</div>
</div>
);
}

render() {
return (
<div className="col-xs-6 col-sm-4">
{this.renderTodo()}
</div>
);
}


TodoForm.index.js:

class TodoForm extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);

this._handleSubmit = this._handleSubmit.bind(this);
}

_handleSubmit(formData) {
console.log('');
console.log('OG: ', this.props.todo)
console.log('formData: ', formData);
const data = this.props.todo.update('title', formData.get('title'));
console.log('data: ', data);
console.log('');
// this.props.updateTodo(data);
}

render() {
const { handleSubmit, pristine, submitting } = this.props;
return (
<form className={`${styles.todoForm} card`} onSubmit={handleSubmit(this._handleSubmit)}>

<div className="card-block">
<label htmlFor="title">{this.props.todo.get('title')}</label>
<div className={'form-group'}>
<Field name={`title`} component="input" type="text" placeholder="Enter new title" className="form-control" />
</div>
</div>

<div className="card-block btn-group" role="group">
<button
className="btn btn-success"
type="submit"
disabled={pristine || submitting}
>
Submit
</button>
<button
className="btn btn-danger fa fa-times"
onClick={this.props.changeTodoEditMode}
>
</button>
</div>

</form>
);
}
}

const randomNum = (() => {
const thing = Math.floor(Math.random() * 1000) + 1;
console.log('thing: ', thing);
console.log('notThing: ', TodoForm.props);
return thing;
})();

export default reduxForm({
form: `todo-${randomNum}`,
})(TodoForm);

Answer

For giving your forms dynamic key you should use form attribute on your TodoForm component:

renderTodo() {
    if (this.state.editMode) {
      return (
        <TodoForm
          form={'todo-' + this.props.todo.id}  
          todo={this.props.todo} changeTodoEditMode={this.changeTodoEditMode}
          updateTodo={this.props.updateTodo}
        />
      );
    }
 [...]
}

(Instead of this.props.todo.id could be your randomNum function call)

API reference: http://redux-form.com/6.0.2/docs/api/Props.md/

Comments