Reputation: 93
The react code below gets a data snapshot from Firebase the first time it runs. The this.state.term
refers to the index of a node in my Firebase Database. My handleChoice
function does a this.setState
, but it doesn't seem to re-render this component to grab the new data snapshot.
The value of this.state.term
does not change under componentDidMount
. However, when I console log it down in the render, it does.
import React, { Component } from 'react';
import NavBar from './navbar.js';
import TermInfo from './termInfo.js';
import * as firebase from 'firebase';
import './App.css';
class App extends Component {
constructor(){
super();
this.state = {
term: 66, //sample term to load first
examples: [1,2,3],
dataNode: {},
}
}
componentDidMount(){
let rootRef = firebase.database().ref('vocab/').child(this.state.term);
rootRef.on('value', snap => {
this.setState({
dataNode: snap.val(),
examples: Object.assign([], snap.val().examples)
});
});
}
_handleChoice(theChosenOne){
this.setState({
term: theChosenOne
});
}
render() {
console.log(this.state.term); //this gives me the right index
return (
<div className="App">
<div className="App-header">
<NavBar choose={this._handleChoice.bind(this)}/>
</div>
<div className='container'>
<div className="jumbotron"><TermInfo node={this.state.dataNode} examples={this.state.examples}/>
<p className="App-intro">
Enter a vocabulary term to search for and add examples!
</p></div>
</div></div>
);
}
}
export default App;
Why does it not rerender and grab the data anew?
Upvotes: 1
Views: 2120
Reputation: 15632
The key point is componentDidMount
will be executed only once in the life cycle of React component, and that will be right after the initial render. Never again.
Quoting from the official Doc:
componentDidMount()
is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
Seems like handleChoice
is triggered based on some events (result of some user interactions). That means we will need to call the API call on each invocation of handleChoice
.
Also we don't need a state to maintain the chosen index. Instead let's directly use the index received by handleChoice
to take firebase snap.
So our component will look like:
class App extends Component {
constructor(){
super();
this.state = {
examples: [1, 2, 3],
dataNode: {},
};
}
componentDidMount() {
this.getData(66); // sample term to load first
}
getData(index) {
const rootRef = firebase.database().ref('vocab/').child(index);
rootRef.on('value', snap => {
this.setState({
dataNode: snap.val(),
examples: Object.assign([], snap.val().examples)
});
});
}
_handleChoice(theChosenOne) {
this.getData(theChosenOne);
}
render() {
console.log(this.state.term); //this gives me the right index
return (
<div className="App">
<div className="App-header">
<NavBar choose={this._handleChoice.bind(this)} />
</div>
<div className='container'>
<div className="jumbotron">
<TermInfo node={this.state.dataNode} examples={this.state.examples}/>
<p className="App-intro">
Enter a vocabulary term to search for and add examples!
</p>
</div>
</div>
</div>
);
}
}
Upvotes: 4
Reputation: 6144
Your componentDidMount
will not be called after the state changes (_handleChoice
) so you won't see changes in the generated html. Try setting the state and the firebase related configuration in componentWillUpdate
instead, since you want it to always get the latest this.state.term
. See react's docs.
Upvotes: 0