REACT: Should HTML forms be Controlled or Uncontrolled components? - javascript

I am having a dilemma as to where Where My Form State Should Live.
React Docs mention:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy). Either the common
owner or another component higher up in the hierarchy should own the
state. If you can’t find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
I have a simple comment UI.
The user enters a comment in the text area.
The user enters name in input field.
The user clicks post and the comment is displayed below.
Components Highrarchy is as follows:
- CommentSection.jsx ---> main (should "Post" state (?))
-comment-post.jsx ---> User Comment Form
- comment-table.jsx
- comment.jsx
- comment-avatar.jsx
- comment-body.jsx
Issue: In comment-post.jsx the input and textarea fields have onChange() event handler, and there is also a click event handle on submit post button. I can choose to do one of two:
In comment-post.jsx when onChange is trigger send the commentBody to CommentSection.jsx The issue here would be I will be sending the commentBody as soon as user types, then sending name later when it triggers and so on
In comment-post.jsx when onChange is trigger SAVE the value in state, then name the name field in the state, when user clicks submit button send to CommentSection.jsx Benefit is Fields are set first in state, and when the user clicks post they are sent to the parent (As it should be right?)
Should the input fields of the comment i.e. commentBody and Name be
saved in the comment-post.jsx (form component) or the parent?
Right now the state I am doing the 2. Saving form fields in state and then sending the state values on submit.
I think the issue is onChange and onClick are two different handlers, the question is which one should pass the values to parent component ideally?
class CommentSection extends Component {
state = {
posts: []
};
handleCommentPost = post => {
const posts = this.state.posts;
posts.push(post);
this.setState({ posts });
};
render() {
console.log("comment:", this.state.posts);
return (
<React.Fragment>
<h1>comments</h1>
<div className="row bootstrap snippets">
<div className="col-md-6 col-md-offset-2 col-sm-12">
<div className="comment-wrapper">
<Comment_Post onClick={this.handleCommentPost} />
<Comment_Table comments={this.state.posts} />
</div>
</div>
</div>
</React.Fragment>
);
}
}
class Comment_Table extends Component {
render() {
const posts = this.props.comments;
let count = 0;
return (
<div className="panel panel-info">
<hr />
<ul className="media-list">
{posts.map(post => (
<Comment key={count++} comment={post.commentBody} />
))}
</ul>
</div>
);
}
}
class Comment extends Component {
render() {
return (
<li className="media">
<Comment_Avatar userAvatar={this.props.commentAvatar} />
<Comment_Body userComment={this.props.comment} />
</li>
);
}
}
class Comment_Body extends Component {
render() {
const { userComment } = this.props;
return (
<div className="media-body">
<span className="text-muted pull-right">
<small className="text-muted">30 min ago</small>
</span>
<strong className="text-success">#MartinoMont</strong>
<p>
{userComment}
{}
</p>
</div>
);
}
}
class Comment_Post extends Component {
state = {
commentBody: null
};
onCommentChange = e => {
this.setState({ commentBody: e.target.value });
};
onCommentPost = e => {
const commentBody = this.state.commentBody;
if (commentBody !== null) {
this.props.onClick({ commentBody });
}
};
onNameInput = e => {};
onCommentPostError() {}
render() {
return (
<React.Fragment>
<div className="panel-heading p-heading">Comment panel</div>
<div className="panel-body">
<textarea
onChange={this.onCommentChange}
className="form-control"
placeholder="write a comment..."
rows="3"
/>
<label htmlFor="fname" onChange={this.onNameInput}>
Name:{" "}
</label>
<input id="fname" placeholder="John" />
<br />
<button
type="button"
className="btn btn-info pull-right"
onClick={this.onCommentPost}
>
Post
</button>
<div className="clearfix" />
</div>
</React.Fragment>
);
}
}
class Comment_Avatar extends Component {
render() {
return (
<a href="#" className="pull-left">
<img
src="https://bootdey.com/img/Content/user_1.jpg"
alt=""
className="img-circle"
/>
</a>
);
}
}

I think the issue is onChange and onClick are two different handlers, the question is which one should pass the values to parent component ideally?
There are two types of Forms that we use in standard User interface design. First is when any change in the Input saves the change. Second, when after making changes to the Form Elements, you press a submit button and then the changes are saved.
Since you have implemented the second type, your onChange should deal with controlling your TextArea state and your onClick should deal with the submit. So your code is just fine.
I am having a dilemma as to where Where My Form State Should Live.
That depends...In your case you only have one Form, and two Form Elements none of which are re-usable. So these uncontrolled Forms work well for you. However, if you wanted to have a Form Component that is re-usable, or if you wanted to have a Form with 15 Fields in it, you would not want to write a separate onChange handler for each one of them. For this, you would want to make a controlled Form Component that can handle all these things for you. Here's an example of what that would look like.
export default class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
values: {}
};
}
#boundMethod
handleSubmit(event) {
event.preventDefault();
this.props.submit(this.state.values);
}
#boundMethod
handleChange(event) {
const { name, value } = event.target;
const newValues = Object.assign(
{ ...this.state.values },
{ [name]: value }
);
this.setState({
values: newValues
});
}
public render() {
const { values } = this.state;
return (
<form onSubmit={this.handleSubmit} noValidate={true}>
<div>
{React.Children.map(
this.props.children,
child => (
{React.cloneElement(child, {
value: values[child.props.name],
onChange: this.handleChange
})}
)
)}
<div>
<button type="submit">
Submit
</button>
</div>
</div>
</form>
);
}
}
Then you will be able to use this Form class like so:
<Form
submit={values => {
/* work with values */
}}
>
<input type="hidden" name="name" />
<input type="hidden" name="rating" />
</Form>;

Related

Changing state of one component from another component in another file

I am new to React and web dev in general.
I have created a Component containing list of calculator like buttons which is stored in Buttons.js.
There is another component called as Submit stored in Submit.js. Submit component is basically a textbox in which we type a mathematical expression which I want to process later.
Both of these components are then called in another component call Leftwindow.js.
So my question is,
How can I make clicking in Buttons component affect the textbox in Submit component. I know it could be done easily had the buttons and input box been the part of a single component.
Basically if I press the '1' button I want it to be added to the input box.
A snapshot of how it looks -
Overview
Code for Buttons.js -
class Buttons extends Component {
constructor(props){
super(props);
this.state = {
//buttonrows//
};
}
render(){
const row1elems = this.state.row1.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row2elems = this.state.row2.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row3elems = this.state.row3.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row4elems = this.state.row4.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
return (
<div className="center">
<ButtonGroup>
{row1elems}
</ButtonGroup>
<ButtonGroup>
{row2elems}
</ButtonGroup>
<ButtonGroup>
{row3elems}
</ButtonGroup>
<ButtonGroup>
{row4elems}
</ButtonGroup>
</div>
);
}
}
export default Buttons;
Code for Submit.js -
class Submit extends Component{
constructor(props){
super(props);
this.state = {
fx: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event){
const target = event.target;
const val = target.val;
const name = target.name;
this.setState({
[name]: val
})
}
handleSubmit(event){
}
render(){
return(
<div>
<Form onSubmit={this.handleSubmit}>
<FormGroup row>
<Col md={12}>
<Input type="text" id="fx" name="fx" placeholder="Type Here" value = {this.state.fx} onChange={this.handleInputChange} />
</Col>
</FormGroup>
</Form>
<Button type="submit" color="primary">Differentiate</Button>
</div>
)
}
}
export default Submit;
Code for LeftWindow.js --
import React, { Component } from 'react';
import Buttons from './Buttons';
import './Custom.css';
import Submit from './Submit';
class LeftWindow extends Component{
constructor(props){
super(props);
}
render(){
return(
<div className="col-3 bg-dark fullheight">
<Buttons/>
<h3 className="center">Enter the function to be differentiated</h3>
<Submit/>
</div>
);
}
}
export default LeftWindow;
This is how your common ancestor file will look like -
Added state having input.
Added a callback "handleInputChange" which updates this input state.
Passed this callback to Both the components.
In your Submit.js file you will need to change your input tag with this
<Input
type="text"
value={this.props.input}
onChange={e => {
this.props.handleInputChange(e.target.value);
}}
/>
Also in your Buttons.js file call this.props.handleInputChange on Button click.
<Button
onClick={() => {
this.props.handleInputChange(button.label)
}}
color={colours[button.type]}
className="buttonsize"
>
{button.label}
</Button>
That's it.
Hope I could help!
In React you work with tree of components where data can travel from top to bottom. It is possible to notify parent component about changed data via passed callbacks. If you have two components that have common ancestor, you can share data between them through this common ancestor.
Let's say you have component Parent which renders your Buttons and Submit components. If you store your data (or state) in Parent and pass this data as props with callbacks, your components then can notify Parent about things happened and parent can change it's state and pass new state as props to children.
There is a "state management" solutions when your data lives detached of your components and injected on one by one basis. In such you won't need parent to store the data, but if we talk about pure react - to share data between branches in react tree, this branches should have common ancestor somewhere in the tree.

Why is my state not updating as expected when creating a comment section?

Im trying to create a comment secton, where this.state stores all comments made by combining the old state with the new one each time a new comment is submitted. However, im getting very strange behavior in my app which i cant explain. On the first comment submital the comment is sent up from commentForm to it's parent commentSection using a callback which invokes the parent function handleCommentSubmit, updating my comments state. Everything renders correctly . However, once the second comment is submitted, the same process occurs; the state is updated, but it doesn't contain the previous first comment. Following this state update, the new state is sent to commentList to render the comments. In this case, strangely, the prop this.state.comments passed down to it now contains the an array containing 2 of the second comment (see bottom for screenshot). This results in my comment section now showing User 2's comment two times, without displaying User 1's comment. Does anyone know why this is happening?
Here is the relevent code:
Parent:
class CommentsSection extends React.Component{
constructor(props){
super(props)
this.state={comments:[], loading:false}
}
componentDidMount(){
}
handleCommentSubmit = (newComment) =>{
var comments = this.state.comments;
var newComments = comments.concat([newComment]);
this.setState({comments: newComments},console.log('The current state is now',this.state.comments));
//comment is object with author and message. Add new comment to old comments
//this.setState({comments:[...this.state.comments,newComment]},console.log(this.state, 'state updated'))
}
//Comments are create in comment form, passed up then sent down through commentList to individual comment rendering inside comment.js
// comment form oncommentsubmit running everytime it renders, not only on submital
render(){
const loadingSpin = this.state.loading ? "App-logo Spin" : "App-logo";
return(
<div>
<span><h4> Comments </h4></span>
<div className="ui grid">
<div className = "right floated eight wide column" >
<CommentList comments={this.state.comments}/>
</div>
<div className="left floated eight wide column">
<CommentForm onCommentSubmit={this.handleCommentSubmit}/>
</div>
</div>
</div>
)
}
}
export default CommentsSection
Children:
function CommentList ({comments}){
//need to map over array of comments to format correctly
console.log('This is what is passed as props to CommentList', comments)
comments = comments.map((comments)=>{return <Comment key = {comments.message} message={comments.message} author={comments.author} />})
return(<div>{comments}</div>)
}
export default CommentList
class CommentForm extends React.Component{
constructor(props){
super(props)
this.comment={author:'', message:''}
}
handleSubmit= (e)=>{
e.preventDefault()
var authorVal = this.comment.author;
var textVal = this.comment.message;
//this stops any comment submittal if anything missing
if (!textVal || !authorVal) {
return;
}
this.props.onCommentSubmit(this.comment);
//reset form values
e.target[0].value = '';
e.target[1].value = '';
}
handleFormChange= (e)=>{
e.preventDefault()
if(e.target.name==='author'){
var author = e.target.value.trim();
this.comment.author = author
}else if(e.target.name==='message'){
var message = e.target.value.trim();
this.comment.message = message
}
}
render() {
return (
<form className = "ui form" method="post" onChange={(e)=>{this.handleFormChange(e)}} onSubmit={(e)=>{this.handleSubmit(e)}}>
<div className="form-group">
<input
className="form-control"
placeholder="user..."
name="author"
type="text"
/>
</div>
<div className="form-group">
<textarea
className="form-control"
placeholder="comment..."
name="message"
/>
</div>
<div className="form-group">
<button disabled={null} className="btn btn-primary">
Comment ➤
</button>
</div>
</form>
);
}
}
this.setState(prevState => ({comments: [...prevState.comments, newComment]}))
The problem.
You are storing author and message on instance variable ( this.comment) of the <CommentForm /> component.The this.comment maintains the same memory location even after re-renders. So even after re-rendering, it's the same object. You are just overwriting the properties of that object. And your parent state is a comments array, where each element is just a pointer to the same this.comment object that gets overwritten every time.
Have a look at the link, keep adding comments to see what's happening to the parent component's state
https://stackblitz.com/edit/react-cubmcf
Solution
Modify handleSubmit to like this.
handleCommentSubmit = newComment => {
var comments = this.state.comments;
// var newComments = comments.concat([newComment]);
this.setState({
comments: [...comments, newComment]
});
};
Update Comment form to use state rather than instance variables.
Changes made
Removed update dom element directly.
Used controlled components for input fields
Some general refactors.
class CommentForm extends React.Component {
constructor(props) {
super(props);
this.state = { author: "", message: "" };
}
handleSubmit = e => {
e.preventDefault();
const {author, message} = this.state;
//this stops any comment submittal if anything missing
if (!author || !message) {
return;
}
this.props.onCommentSubmit({ author, message });
this.setState({author: "", message : ""})
};
handleFormChange = e => {
e.preventDefault();
this.setState({[e.target.name]: e.target.value});
};
render() {
const {author, message} = this.state;
return (
<form
className="ui form"
method="post"
onChange={e => {
this.handleFormChange(e);
}}
onSubmit={e => {
this.handleSubmit(e);
}}
>
<div className="form-group">
<input
className="form-control"
placeholder="user..."
name="author"
type="text"
value = {author}
/>
</div>
<div className="form-group">
<textarea
className="form-control"
placeholder="comment..."
name="message"
value={message}
/>
</div>
<div className="form-group">
<button disabled={null} className="btn btn-primary">
Comment ➤
</button>
</div>
</form>
);
}
}

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:
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);
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/

React changing parent state during input onChange getting stuck

I am building a huge form which is made of atleast 50 inputs.
I have wrote a function in the form component that will save the value of the input to the form state:
PARENT FUNCTION
saveToState(details) {
const { company } = this.state;
company[details.part][details.element] = details.value;
this.setState({ company });
}
PASSING TO CHILD COMPONENT (INPUT)
<FieldInput
label="Name (as shown) *"
part="information"
element="displayName"
saveToState={this.saveToState}
/>
Here is the Input component:
import React, { Component } from 'react';
export default class FieldInput extends Component {
render() {
const { label, part, element, saveToState } = this.props;
return (
<div className="field">
<label>{label}</label>
<div className="ui input">
<input
type="text"
name={`${part}[${element}]`}
onChange={(e) => saveToState({
part,
element,
value: e.target.value
})}
/>
</div>
</div>
);
}
}
In result whenever I type something in the input It's taking it 200-300ms to really display what I wrote in the input, the state is getting updates instantly but whenever I type a character I set the new state of the parent form and update it which updates the whole component. The only way i found around it is to use saveToState within the parent component without passing it down. but that would require 1000's of line of code, Is there any way around this? Thanks!
There are alot of ways you can solve this problem. The easiest one and the fastest one is to use onBlur instead of onChange that way setState will happen not when you key pressing in the input but when the input loses focus.
import React, { Component } from 'react';
export default class FieldInput extends Component {
render() {
const { label, part, element, saveToState } = this.props;
return (
<div className="field">
<label>{label}</label>
<div className="ui input">
<input
type="text"
name={`${part}[${element}]`}
onBlur={(e) => saveToState({
part,
element,
value: e.target.value
})}
/>
</div>
</div>
);
}
}

Working with the same ReactJS form component on the same page

I am fairly new to reactJS and I am wondering what's the best way to handle having the same form component on a single page. Please keep in mind that i am using flux and the component is talking to a store.
For example:
< SearchForm />
< SearchForm />
When I try to use form #1 input field, Form #2 gets the value from form #1 at the same time. i think the problem is coming from the store. the components are speaking to the same store and the store is updating all the components at once.
How can i handle this problem?
here is the code i have so far.
const SearchField = React.createClass({
propTypes: {
isSearchActivated: React.PropTypes.bool.isRequired,
},
_onChange() {
var previousHighlightedIndex = this.state.highlightedIndex;
this.setState(getStateFromStores(), function() {
if (previousHighlightedIndex == 0 &&
this.state.highlightedIndex == -1) {
this.refs.SearchBar.selectAll();
}
});
},
componentDidMount() {
if (window.location.pathname == "/" && !Modernizr.mq("screen only and (max-width: 768px)")) {
$(ReactDOM.findDOMNode(this.refs.SearchBar)).find("input").focus();
}
},
componentWillUnmount() {
SearchResultStore.removeChangeListener(this._onChange);
},
onChangeSearchString(e) {
SearchResultsUtils.search(e.target.value);
},
onBlur(e) {
var self = this;
var cb = function() {
if (!self.state.selectedResult && self.state.results.length) {
self.handleSelectedResult(0);
}
SearchResultsActions.disallowResultsDisplay();
};
if($(".search-bar").hasClass("active")) {
$(".search-bar.active").removeClass("active");
}
},
onFocus(e) {
$(ReactDOM.findDOMNode(this.refs.SearchBar)).closest(".search-bar").addClass("active");
},
handleSubmit() {
var self = this;
},
render() {
var className = "search-bar clearfix";
return (
<div className={className}>
<div className="search-bar-search">
<SearchBar
searchString={this.state.searchString}
onChange={this.onChangeSearchString}
onKeyDown={this.onKeyDownSearchString}
onFocus={this.onFocus}
onBlur={this.onBlur}
placeholder="Search Meds or Conditions"
ref="SearchBar" />
</div>
<SearchButton
handleSubmit={this.handleSubmit} />
</div>
);
},
});
module.exports = SearchField;
Thanks for the help in advance.
Definitely you can reuse your components multiple times across your application.
1.If you do not want to have any state:
In your situation you can provide form submit handler as callback prop. If you do not want to maintain any state on your search form.
e.g
For form 1
<Searchform submitHandler={searchForm1Handler}/>
For form 2
<Searchform submitHandler={searchForm2Handler}/>
and Inside your search form component
render(){
return (
<form id="searchform" onSubmit={this.props.submitHandler} role="form">
// other input and buttons
</form>
)
}
2. With states
With this approach each component will have its own separate states which will be private to them.
Here is sample component to illustrate this
import React ,{ Component } from 'react';
export default class SearchForm extends Component {
constructor(props){
super(props);
this.state = {
searchTerm : ''
};
this.submit = this.submit.bind(this);
this.changeSearchTerm = this.changeSearchTerm.bind(this);
}
submit(e){
e.preventDefault();
let searchTerm = this.state.searchTerm;
//Now perform some action based on search term you get
}
changeSearchTerm(e){
this.setState({searchTerm :e.target.value});
}
render(){
return(
<div>
<div className="row">
<div className="col-md-12">
<form role="form" className="form-horizontal" onSubmit={this.submit}>
<fieldset>
<div className="form-group">
<div className="col-sm-6">
<input id="st" type="text" placeholder="search term" onChange={this.changeSearchTerm} value={this.state.searchTerm} required autofocus/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12 text-center">
<button className="btn">
Search
</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
)
}
}
Now to use them
For form 1
<Searchform/>
For form 2
<Searchform/>
React has stateful components:
a component can maintain internal state data (accessed via this.state)
which allows you to have <SearchForm />#1 with value1, and <SearchForm />#2 with value2 stored in this.state
To build a form, check this: https://facebook.github.io/react/docs/forms.html
It looks like your store only saves one state - which includes search value - for one form.
If you render this form twice, then both forms retrieve the same state on each change. So they are an exact copy of each other by design. If one form changes, both forms notice the change, and both forms will retrieve the exact same state from the store.
To fix this, I would suggest:
change the store so that it can save a state for multiple forms, each with its own ID
pass this ID to each form as a prop, e.g. <SearchForm formID='form1'/> <SearchForm formID='form2'/>
pass the formID down as a prop to all children of the form, such as <SearchField>
inside the <SearchField>, make sure your component only renders state from its own formID. You probably need to update the getStateFromStores() method itself. Or inside <SearchField> filter out relevant stuff first, before passing it to setState().
when your component informs the store that something has changed (I guess this is what SearchResultsUtils.search(e.target.value); does), then you need to pass the formID as well. So the store will know which form to update.
PS: I think your code is missing some lines in componentDidMount(): you have code to remove the listener to store changes, but your code to add the listener is missing. The code to invoke the _onChange() method is missing from your component.

Categories