Basically new to React, I'm a bit confused on how to properly pass states between components. I found a similar question already React – the right way to pass form element state to sibling/parent elements? but I wonder if you can give me a specific answer for the code below.
Currently the structure of the app includes:
App
SearchBar and RecipesList
The goal is to make an async search on my Meteor collection and display only the recipes that match the search term.
Right now, I'm just showing all the recipes in my Meteor collection.
I've created a stateful component named SearchBar which holds the input value as this.state.term. The idea is to pass the state to RecipesList but I'm not sure if it's the right thing to do. Alternatively I'd let App deal with the state and passing it to the childs. I believe this is a very common scenario, how do you do it?
App
class App extends Component {
render( ) {
return (
<div>
<Navbar/>
<SearchBar/>
<RecipesList/>
</div>
);
}
}
SearchBar
export default class SearchBar extends Component {
constructor( props ) {
super( props );
this.state = {
term: ''
};
}
onInputChange( term ) {
this.setState({ term });
}
render( ) {
return (
<div className=" container-fluid search-bar">
<input value={this.state.term} onChange={event => this.onInputChange(event.target.value.substr( 0, 50 ))}/>
Value: {this.state.term}
</div>
);
}
}
RecipesList
const PER_CLICK = 5;
class RecipesList extends Component {
componentWillMount( ) {
this.click = 1;
}
handleButtonClick( ) {
Meteor.subscribe('recipes', PER_CLICK * ( this.click + 1 ));
this.click++;
}
renderList( ) {
return this.props.recipes.map(recipe => {
return (
<div key={recipe._id} className="thumbnail">
<img src={recipe.image} alt="recipes snapshot"/>
<div className="caption">
<h2 className="text-center">{recipe.recipeName}</h2>
</div>
</div>
);
});
}
render( ) {
return (
<ul className="list-group">
{this.renderList( )}
<div className="container-fluid">
<button onClick={this.handleButtonClick.bind( this )} className="btn btn-default">More</button>
</div>
</ul>
);
}
}
// Create Container and subscribe to `recipes` collection
export default createContainer( ( ) => {
Meteor.subscribe( 'recipes', PER_CLICK );
return {recipes: Recipes.find({ }).fetch( )};
}, RecipesList );
App
class App extends Component {
constructor(props, ctx){
super(props, ctx)
this.state = {
searchQuery: ''
}
this.searchInputChange = this.searchInputChange.bind(this)
}
searchInputChange(event) {
this.setState({
searchQuery: event.target.value.substr( 0, 50 )
})
}
render( ) {
const { searchQuery } = this.state
return (
<div>
<Navbar/>
<SearchBar onChange={this.searchInputChange} value={searchQuery}/>
<RecipesList searchQuery={searchQuery}/>
</div>
)
}
}
The App component takes care of the state and this is then passed down to the children as props the seach term is available to RecipesList through props.searchQuery.
The searchInputChange handler is passed down to the SearchBar as props.
SearchBar
export default const SearchBar = ({value, onChange}) => (
<div className=" container-fluid search-bar">
<input value={value} onChange={onChange}/>
Value: {value}
</div>
)
Since the SearchBar delegated state to the parent component, we can use a stateless react component as we only need information from the props to render it.
In general it is always best to have a logical or stateful or controller component take care of state and the logic, this component then passes down state and methods to presentational or view components which take care of what the user sees and interacts with.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With