【问题标题】:Which lifecycle event to use to read first item使用哪个生命周期事件来读取第一项
【发布时间】:2017-04-11 02:46:38
【问题描述】:

我有一个 React 组件,它显示 NBA 球队的列表和有关他们的信息。

我创建了一个位于左侧并提供团队列表的 TeamsList 组件。然后我有一个 TeamInfo 组件,它提供有关“选定”团队的信息。

在 MyMainComponent 的父入口点,我读取了团队列表并将其放入我的 redux 存储中。

这是我想要弄清楚的:我想自动进行 API 调用以获取有关列表中第一个团队的信息。我应该在 MyMainComponent 中为此使用哪个生命周期事件,例如ComponentWillMount、ComponentDidMount 甚至是构造函数?

class MyMainComponent extends Component {

   render() {

      const teams = this.props.teamsList;
      const team = this.props.team;

      return(

         <div>
            <div class="leftColumn">
               <TeamsList data={teams} />
            </div>
            <div class="rightColumn">
               <TeamInfo data={team} />
            </div>
         </div>

      );

   }

}

function mapStateToProps(state) {
    return {
        teamsList: state.teamsList,
        team: state.team
    };
}

export default connect(mapStateToProps)(MyMainComponent)

更新: 我现在正在使用下面的代码在我的操作中处理这个问题,但由于某种原因,我无法拯救我所在州的第一支球队。我将团队的初始状态设置为一个空对象。我通过我的操作/reducer 来更新状态,但由于某种原因,即使teamList 中有正确的数据,我仍然为团队获取一个空对象。为了阐明我在做什么,在入口点,我只需调用 getTeamsList() 函数,它会自动调用 getTeam() 函数。我知道这工作正常。我也知道我正在使用 reducer 来更新 teamsList 和 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 setIsLoading = (value) => {
   return{
      type: types.SET_IS_LOADING,
      value
   };
}

export const getTeamsList = () => {

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


export const getTeam = (id) => {

    return (dispatch) => {

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

这是我的减速器:

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
            })

        case types.SET_IS_LOADING:
           return Object.assign({}, state, {
              value: action.value
           })

        default: return state
    }
}

更新 2: 看起来正在执行的操作顺序不是我需要的方式。我以为我可以通过将它们包装在另一个函数中来控制序列,然后同步运行,但这也不起作用。

如果您查看我为使事情顺利进行而调用的 initTeams 函数,您会发现 setIsLoading 步骤首先执行——实际上是背靠背。我看到代码首先点击减速器将 isLoading 设置为 true,然后立即将其设置为 false。然后它执行 getTeamsList()。从那里它实际上进入了 TeamInfo 组件的渲染功能。完成这一切后,它会调用 getTeam() 函数将第一个团队加载到商店中。

更新 3: 差不多了,但仍需要澄清。我现在更好地理解了thunks。仍然让我感到困惑的一件事是:我更新了我的 getTeamsList() 函数,我希望它按以下顺序执行操作:

  1. 进行 API 调用以获取团队列表
  2. 在商店中设置团队列表
  3. 在存储中将 isLoading 设置为 True
  4. 如果我有团队,请调用 getTeam() 操作以获取团队信息
  5. 在商店中设置团队信息
  6. 将 isLoading 设置为 False

相反,它会在第 3 步之后执行第 6 步,然后是第 4 步和第 5 步。

最终,我得到了我想要的结果,但我想了解如何让它以正确的顺序执行我想要执行的步骤。

【问题讨论】:

  • 你有 SSR(服务器端渲染)吗?

标签: reactjs redux


【解决方案1】:

由于您使用的是 Redux,我建议您将 API 调用的任务委托给您的动作创建者并从高阶组件 (HOC) 分派它们

我认为组件应该与业务逻辑和 API 数据无关。它们应该只代表我们 MVC 应用程序中的 Viewsee WHY?

请参阅此answer,我在这里解释如何使用redux-connect 中的async-connect 来加载我们应用程序的初始数据。


如果您坚持在组件内部进行 API 调用,请在 componentDidMount 中执行此操作。

引用另一个SO answer

componentDidMount 用于副作用。添加事件侦听器、AJAX、 改变 DOM 等。

componentWillMount 很少有用;特别是如果你关心 服务器端渲染(添加事件监听器会导致错误和泄漏, 以及许多其他可能出错的东西)。

有人谈论从课堂上删除componentWillMount 组件,因为它的用途与constructor 相同。它 将保留在createClass 组件上。

当您使用 componentWillMount 进行 API 调用时,组件渲染可能会延迟(我们将数据提取到 setState 并将用于在视图中显示的常见用例)。

另一个问题:

componentWillMount 在服务器上运行,但componentWillUnmount (您删除听众的地方)不是。这将导致您添加 听众,永远不要清理他们。


更新

关于错误的调用动作顺序:

getTeamsListgetTeam 应该是async actions,基本上它们应该是return 一个承诺。

返回的承诺控制 .then 附加到承诺链。所以如果你错过了,你将无法预测序列。

我们在这里缺少returns:

export const getTeamsList = () => {
    return (dispatch) => {
        fetch('/api/teams', fetchOptionsGet())   ----> missing 'return' before 'fetch'
        // ...
    };
}


export const getTeam = (id) => {
    return (dispatch) => {
        fetch('/api/team/' + id, fetchOptionsGet())   ----> missing 'return' before 'fetch'
        // ...
    };
}

由于我们使用箭头函数,我们不一定需要return,而是可以这样做(删除花括号 {}):

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


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

【讨论】:

  • 感谢您的回复,我同意您的建议。我实现了它,但由于某种原因,即使我能够将teamList 提交到我的商店,我似乎也无法成功存储第一个团队。当我单步执行代码时,我认为它实际上是在存储它,但是当我尝试从我的组件访问第一个团队信息时,我不断得到一个空对象,这是我在 initialState 中设置的。有没有可能像 setState 的情况一样,更新不是立即的?我用我的代码在我的原始帖子中添加了一个更新部分。
  • “有没有可能像 setState 的情况一样,更新不是立即的?”。不,redux 存储更新是同步的。但是您的动作创建者getTeamgetTeamList 是异步API 调用,只有在promise 解决(API 响应)之后,它们才会调用它们的reducer 类型来更新全局状态。这对您现在面临的问题有任何意义吗?
  • 进一步检查表明我实际上正在执行所有步骤并将数据减少到存储中,但它没有及时发生。看起来我在第一个团队数据提交到商店之前点击了团队组件的渲染功能。
  • 我在原始帖子中添加了第二个更新。看起来我需要一种控制动作执行顺序的方法。
  • 我已经很接近了。我现在更好地理解了 thunk 并在我的场景中使用它们。但是,顺序仍然不完全是我认为应该的。我添加了更新 3。我错过了什么?为什么我没有得到我想要的顺序?
猜你喜欢
  • 1970-01-01
  • 2019-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-20
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多