Sam Sam - 11 days ago 7
React JSX Question

Which lifecycle event to use to read first item

I have a React component that displays a list of NBA teams and information about them.

I created a TeamsList component that sits on the left and provides a list of teams. I then have a TeamInfo component that provides information about "selected" team.

At the entry point which is the parent to MyMainComponent, I read teams list and put it into my redux store.

Here's what I'm trying to figure out: I want to automatically make an API call to get the information about the first team in the list. Which life cycle event should I use for this in the MyMainComponent e.g. ComponentWillMount, ComponentDidMount or even the constructor?

class MyMainComponent extends Component {

render() {

return(

<div>
<div class="leftColumn">
<TeamsList />
</div>
<div class="rightColumn">
<TeamInfo />
</div>
</div>

);

}

}


UPDATE:
I'm now handling this in my actions using the code below but for some reason I'm not able to save the first team in my state. I set the initial state for the team to an empty object. I go through my actions/reducer to update the state but for some reason, I keep getting an empty object for the team even though teamsList has the correct data in it. To clarify what I'm doing, at the entry point, I simply call the getTeamsList() function and that automatically calls the getTeam() function. I know this is working fine. I also know that I'm hitting my reducer to update the state both for the teamsList and team.

export const setTeamsList = (teams) => {
return {
type: types.SET_TEAMS_LIST,
teams
};
}

export const setTeamInfo = (team) => {
return {
type: types.SET_TEAM_INFO,
team
};
}

export const getTeamsList = () => {

return (dispatch) => {
fetch('/api/teams', fetchOptionsGet())
.then(parseJSON)
.then(teams => {
dispatch(setTeamsList(teams))
if(teams !== null && teams.length > 0)
dispatch(getTeam(teams[0].teamId))
})
};
}


export const getTeam = (id) => {

return (dispatch) => {

fetch('/api/team/' + id, fetchOptionsGet())
.then(parseJSON)
.then(team => {
dispatch(setTeamInfo(team))
})
};
}


And here's my reducer:

import 'babel-polyfill';
import * as types from '../actions/actionTypes';

const initialState = {
teamsList: [],
team: {}
};

export default (state = initialState, action) => {

switch (action.type) {

case types.SET_TEAMS_LIST :
return Object.assign({}, state, {
teamsList: action.teams
})

case types.SET_TEAM_INFO :
return Object.assign({}, state, {
team: action.team
})

default: return state
}
}

Answer

Since you're using Redux, I recommend you to delegate the task of API calls to your action creators and dispatch them from a Higher Order Component (HOC).

I believe components should be agnostic to business logic and API datas. They should only represent the View in our MVC apps. see WHY?

See this answer where I am explaining how to use async-connect from redux-connect for loading the initial data of our App.


If you insist to make the API call inside you component itself, do that in componentDidMount.

Quoting another SO answer:

componentDidMount is for side effects. Adding event listeners, AJAX, mutating the DOM, etc.

componentWillMount is rarely useful; especially if you care about server side rendering (adding event listeners causes errors and leaks, and lots of other stuff that can go wrong).

There is talk about removing componentWillMount from class components since it serves the same purpose as the constructor. It will remain on createClass components.

Component rendering may get delayed when you're using componentWillMount for API calls (a common use case we fetch data to setState and that will be used to show in the view).

Another issue:

componentWillMount is run on the server, but componentWillUnmount (where you remove listeners) isn't. This would cause you to add listeners and never clean them up.