如何在缺少特定部分状态时阻止导航到组件?

时间:2017-07-16 08:35:50

标签: reactjs redux react-router react-redux

首先请原谅措辞不好的问题。

我有一个SPA,其中有一个包含此路线的页面:

/students

此页面的容器为StudentsView.js,我在其中执行此操作以获取学生列表:

  componentDidMount () {
    this.props.actions.loadStudents()
  }

这是一个学生列表,点击一个学生将根据学生ID进行学生的详细信息页面:

/student/5

此页面的组件为StudentDetailView.js

现在为了避免不必要的API调用来提高初始应用程序加载期间的性能,我还没有调度loadStudents()操作,该操作将学生的状态部分设置为API调用,因为此页面不是主要的我的应用的页面。

现在的问题是当用户导航到学生详情页面时说:

/student/5

然后刷新页面,因为它们位于不同的组件上,componentDidMount()组件的StudentsView.js不再被调用,我最终在我的状态中有一个空学生数组,所以在我的学生详细信息组件学生将被定义:

function mapStateToProps (state, ownProps) {
  const studentId = +ownProps.params.id
  let student = { id: '', firstName: '', lastName: '' }
  if (studentId && state.students.length > 0) {
    student = getStudentById(state.students, studentId)
  }
  return {
    student: student
  }
}

除了确保在初始应用加载中调用loadStudensts()操作外,还有办法解决这个问题吗?

最佳做法是,如果students列表为空,我是否会将用户导航回列表页面?有没有办法,是否建议我在loadStudents()组件中解除StudentDetailView.js操作?

基本上,当用户在StudentDetailView.js上时,如何确保学生的部分状态始终填充?

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

我相信您正在使用redux,因此在您的StudentDetail组件中,您可以发送一个操作说loadStudentById(5),在您的操作中,您可以检查loadStudents返回的数据是否可用,如果不是,您可以可以先loadStudents后跟loadStudent()

//somewhere in your actions
function loadStudent(studentId) {
  return (dispatch, getState) => {
    dispatch(loadStudents()).then(() => {
      const students = getState().students.get('students')
      const student = students.find(s => s.id === studentId)

      // This action will set the student in reducer when type is matched
      dispatch(setStudent(student))
    })
  }
}

所以setStudent可能就像

function setStudent(student) {
  return {
    type: 'SET_STUDENT',
    payload: student
  }
}

要检查学生是否已经可用并且在loadStudents中避免API调用,您可以执行类似的操作

function loadStudents() {
  return (dispatch, getState) => {
    const students = getState().students.get('students')

    // Return early avoiding API call
    if (students && students.length > 0) {
      return Promise.resolve()
    }

    // actual API loading logic here
  }
}

答案 1 :(得分:0)

我认为,经过一番思考后实现这一目标的最佳方法是在学生详细信息组件上发送loadStudentById()操作。我可以在componentDidMount()

中执行此操作
  componentDidMount () {
    const studentId = this.props.params.id
    this.props.actions.loadStudentById(studentId)
  }

结果the seed project I'm using有一种独特的方法来异步加载路由,并且他们建议不要使用生命周期钩子来发送动作来检索数据。基本上只在路由匹配后加载组件,此时我可以拦截此挂钩并在加载组件之前调度getStudentById()操作。类似的东西:

import { loadStudentById } from '../../actions/studentActions'

export default (store) => ({
  path : 'student/:id',
  getComponent (nextState, cb) {
    require.ensure([], (require) => {
      const studentId = nextState.params.id
      store.dispatch(loadStudentById(studentId))
      const student = require('./components/StudentDetailView').default
      cb(null, student)
    }, 'student')
  }
})

此处有更多详情:

Populating a Component On Load

这个项目的创造者非常时尚的方法。