Reputation: 2643
I have the following class:
class App extends Component {
constructor(props){
super(props);
this.state = {value: 'currentlyReading'};
this.handleChange = this.handleChange.bind(this);
}
bookOptions = [{
value: 'currentlyReading',
text: 'Currently Reading'
},
{
value: 'wantToRead',
text: 'Want to Read'
},
{
value: 'read',
text: 'Read'
},
{
value: 'none',
text: 'None'
}]
onHandleChange = (e) => {
this.setState({
state: e.target.value
})
};
handleChange(e){
this.setState({value: e.target.value});
}
render() {
return (
<select value={this.state.value} onChange={this.handleChange}>
{
this.bookOptions.map((c) => {
<option value={c.value} >{c.text}</option>
})
}
</select>
);
}
}
If I render the select list with regular html syntax then it works perfectly, where as if I try to create it dynamically then it doesn't load anything. How would you go about creating a list dynamically in react so that it maintains its binding?
Any advice would be appreciated!
Thanks.
Upvotes: 0
Views: 1445
Reputation: 1930
Edit, went a bit beyond scope.
You should set the data as props and add the options to an object to ensure map doesn't freak out when the props are empty.
class App extends Component {
constructor(props){
super(props);
this.state = {value: 'currentlyReading'};
this.handleChange = this.handleChange.bind(this);
}
onHandleChange = (e) => {
this.setState({
state: e.target.value
})
};
handleChange(e){
this.setState({value: e.target.value});
}
render() {
let oOptions = [];
if( this.props.bookOptions ) {
this.props.bookOptions.map((c) => {
oOptions.push( <option key= {c.value} value={c.value} >{c.text}</option> );
});
}
return (
<select value={this.state.value} onChange={this.handleChange}>
{ oOptions }
</select>
);
}
}
App.defaultProps = {
bookOptions: [
{
value: 'currentlyReading',
text: 'Currently Reading'
},
{
value: 'wantToRead',
text: 'Want to Read'
},
{
value: 'read',
text: 'Read'
},
{
value: 'none',
text: 'None'
}]
};
You can either fetch the data from an endpoint at the top level mount and pass them as props:
const resp = await fetch('/givemeprops', ...
const { data } = await resp.json();
<App bookOptions={data} />
Or you can put your ajax call inside any component's componentDidMount react.js lifecycle method and set them as state:
componentDidMount() {
const resp = await fetch('/givemeprops', ...
const { data } = await resp.json();
this.setState({ bookOptions: data });
}
And use them from there.
let bookOptions = [{
value: 'currentlyReading',
text: 'Currently Reading'
},
{
value: 'wantToRead',
text: 'Want to Read'
},
{
value: 'read',
text: 'Read'
},
{
value: 'none',
text: 'None'
}];
app.get('/givemeprops',
res.json({ bookOptions: bookOptions })
);
There are other solutions for managing an app's props/state/interaction with the backend like Flux and Redux. I would recommend checking out graphql as well.
Some great references:
Upvotes: 0
Reputation: 8542
When you are using the curly braces in the following piece of code:
this.bookOptions.map((c) => {
<option value={c.value} >{c.text}</option>
})
You are not returning anything, hence you are rendering an array of undefined elements. Remove that and your code should work as expected. See the working code below.
class App extends React.Component {
constructor(props){
super(props);
this.state = {value: 'currentlyReading'};
this.handleChange = this.handleChange.bind(this);
}
bookOptions = [{
value: 'currentlyReading',
text: 'Currently Reading'
},
{
value: 'wantToRead',
text: 'Want to Read'
},
{
value: 'read',
text: 'Read'
},
{
value: 'none',
text: 'None'
}]
onHandleChange = (e) => {
this.setState({
state: e.target.value
})
};
handleChange(e){
this.setState({value: e.target.value});
}
render() {
return (
<select value={this.state.value} onChange={this.handleChange}>
{
this.bookOptions.map((c) => <option key={c.text} value={c.value} >{c.text}</option>)
}
</select>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Upvotes: 2
Reputation: 1033
I guess you missing key={i}
<select value={this.state.value} onChange={this.handleChange}>
{
this.bookOptions.map((c, i) => {
<option key={i} value={c.value} >{c.text}</option>
})
}
</select>
I just guess, not sure right or wrong.
Upvotes: 1