Jie Jie - 3 months ago 9
React JSX Question

componentDidMount did not fire the render()

I am calling an async function in

componentDidMount()
, I expect after the state got updated with fetched data, the component should re-render, but no.

component code:

function mapStateToProps(state){
return {
posts: state.posts
}
}

function mapDispatchToProps(dispatch){
return bindActionCreators(actionCreators, dispatch)
}

export default class Main extends React.Component{

constructor(props){
super(props)
}

componentDidMount(){
this.fetchData()
}

fetchData(){
this.props.getAllPosts().then(() => {
console.log('props: ' + JSON.stringify(this.props))
this.props.posts.data.map( post => {
console.log(post.content)
})
})
}

render(){
return(
<div>
{!this.props.loaded
? <h1>loading...</h1>
:
<div>
{this.props.posts.data.map(post => {
return(
<div>
<h2>{post.title}</h2>
<p>{post.content}</p>
</div>
)
})}
</div>
}
</div>
)
}
}

const Home = connect(mapStateToProps, mapDispatchToProps)(Main)


action:

export function fetchAllPosts(){
return{
type: 'FETCH_ALL_POSTS'
}
}

export function receivedAllPosts(posts){
return{
type: 'RECEIVED_ALL_POSTS',
post_list: posts
}
}

export function getAllPosts(){
return (dispatch) => {
dispatch(fetchAllPosts())
return fetch('/api/posts')
.then(response => response.json())
.then(json => {
dispatch(receivedAllPosts(json.data))
})
.catch(error => {

})
}
}


reducer:

export function posts(state = {loaded: false}, action){
switch(action.type){
case 'FETCH_ALL_POSTS':
return Object.assign({}, state, {
'loaded': false
})
case 'RECEIVED_ALL_POSTS':
return Object.assign({}, state, {
'data': action.post_list,
'loaded': true
})

default:
return state
}
}


in the
console.log()
in the
componentDidMount()
, I do see the data got fetched, so it means it is in the state, but not applied into the
render()
, i don't know why.

Joy Joy
Answer

It is because of a simple reason: you should use this.props.posts.loaded, instead of this.props.loaded.

When you set your state to props:

function mapStateToProps(state){
    return {
        posts: state.posts
    }
}

Here state.posts is actually the object from your reducer:

{
  'data': action.post_list, 
  'loaded': true
}

So similar to use access your posts list via this.props.posts.data, you should use this.props.posts.loaded. I believe you can debug through debugger or console.log easily.

A live code: JSFiddle