Reputation: 155
Quick and "easy": How do I make my parent component wait for a certain state to be set until I render the child component every time I access the parent (not only once)?
A bit more detailed: I want to be able to filter results based on user input. I have a controller view "Items", that calls a child component called "FilterComponent" and passes the filter state as a prop. I make an initial call from the controller to an action creator, which gets the saved filters from local storage and dispatches them to the store. Based on that result I update the filters state and pass it as a prop to the child.
Where I'm getting stuck is finding a way to make sure that I don't render the "FilterComponent" before the filters state is set. I can get it to work, so that when I refresh the page, it loads the filters - however when I access the page somewhere else and navigate to the Items controller it just gets stuck.
I think it has something to do with the way I'm getting the filters via the action/store, which is called in componentDidMount() - since componentDidMount() is only called once and I kind of need to call it every time.
import React, { PropTypes } from 'react';
import FilterAction from '../../Actions/FilterActions';
import BaseComponent from '../../Components/Base';
import { Form, FormGroup, FormControl, ControlLabel, InputGroup, DropdownButton, MenuItem, Button } from 'react-bootstrap';
import Select from 'react-select';
import _ from 'lodash';
class FilterComponent extends BaseComponent {
constructor(props) {
super(props);
this.state = {};
this._bind(
'handleExperienceChange',
'handlePriceChange',
'applyFilter'
);
}
componentDidMount() {
this.applyFilter();
}
handlePriceChange(price) {
var filter = {};
var product = this.props.product.tag;
filter[product] = {
price: price ? price.value : null
};
let updatedState = _.merge(this.state, {
selectedPrice: price || null
});
this.updateFilter(filter);
this.setState(updatedState);
}
getPrice() {
var price = this.props.lang.select.prices;
var priceSelect = [];
Object.keys(price).map(function (key) {
priceSelect.push({
value: key,
label: price[key]
});
});
return priceSelect;
}
applyFilter() {
var filters = this.props.filters || null;
var product = this.props.product || null;
if (filters && product && filters[product.tag]) {
var selectedPrice = filter.price ? {
value: filter.price,
label: this.props.lang.select.prices[filter.price]
} : false;
}
var updatedState = {
selectedPrice: selectedPrice || false
};
this.setState(_.merge(this.state, updatedState));
}
updateFilter(filter) {
FilterAction.update(filter);
}
render() {
const { product, lang } = this.props;
return (
<div className="filter">
<div className="flex">
<div className="pane-50">
<Select name="price"
placeholder={lang.general.form.input.price}
value={this.state.selectedPrice}
options={this.getPrice()}
onChange={this.handlePriceChange}
searchable={false} />
</div>
</div>
</div>
);
}
}
export default FilterComponent;
Upvotes: 2
Views: 2480
Reputation: 5927
If your state isn't ready, just return null from your render() method. Whenever state or props is updated, render() will run again. Once your state is ready, return your child components as normal.
render() {
const { product, lang } = this.props;
if (!this.state.selectedPrice)
return null;
return (
<div className="filter">
<div className="flex">
<div className="pane-50">
<Select name="price"
placeholder={lang.general.form.input.price}
value={this.state.selectedPrice}
options={this.getPrice()}
onChange={this.handlePriceChange}
searchable={false} />
</div>
</div>
</div>
);
}
Upvotes: 1
Reputation: 1767
Are you getting any errors? Variable filter
seems undefined in your applyFilter()
.
Also, although unrelated, you can set state by calling setState()
with the new keys and values, don't have to create the entire new state:
Instead of:
var updatedState = {
selectedPrice: selectedPrice || false
};
this.setState(_.merge(this.state, updatedState));
You can simply do:
this.setState({
selectedPrice: selectedPrice || false
});
Lastly, consider calling applyFilter()
from componentWillMount()
since it affects how the component is rendered. componentWillMount()
is called before render()
.
To get faster help, consider creating a simplified, isolated, but working example of your project on codepen or similar services.
Upvotes: 1