Исходное состояние не определено в приложении с подключением к серверу

У меня есть приложение ReactJs, которое использует Redux для управления своим магазином. Мое состояние - сложный json, поля которого могут меняться. Когда я запускаю редукторы, мне нужно указать исходное состояние, когда я обращаюсь к нему перед извлечением контента с сервера, я получаю ошибку "undefined". Ниже приведен пример,

//Test.jsx
class Test extends React.Component{
    componentWillMount(){
       fetchContent({
         type: SET_CONTENT
       });
    }
    render(){ 
      return(
       <div> {this.props.header} </div>
      )
    }
    mapStateToProps(state){
       return{
         header: state.reducer1.a.b.c
       }
    }
}
export default (mapStateToProps)(Test);


//reducer1.js
export default function reducer1(state = {}, action = {}) {
  switch (action.type) {
    case SET_CONTENT:
      return Object.assign({}, action.data);
    default:
      return state;
  }
}
  
//reducer2.js
export default function reducer2(state = {}, action = {}) {
  switch (action.type) {
    case SET_SOMETHING_ELSE:
      return Object.assign({}, action.data);
    default:
      return state;
  }
}
  
//CombinedReducer.js
export default combinedReducers(Object.assign({}, {reducer1}, {reducer2}))

Теперь, когда компонент инициализируется в первый раз, state.reducer1.a.b.c бросает неопределенный, потому что fetchContent() на данный момент не вызывается.

Итак, мой вопрос, как мне решить эту проблему? Является ли указание начального состояния в редукторе единственным вариантом? как следующее,

//reducer1.js
export default function reducer1(state = { a: { b: { c: "somedata"}}}, action = {}) {
  switch (action.type) {
    case SET_CONTENT:
      return Object.assign({}, action.data);
    default:
      return state;
  }
}

2 ответа

Вы можете сделать это, но вы также можете просто обновить функцию mapState, чтобы проверить наличие первого ключа.

function mapStateToProps(state){
   return{
     header: state.reducer1.a ? state.reducer1.a.b.c : <div>Loading..</div>
   }
}

Также я предполагаю, что вы хотите иметь mapStateToProps вне определения класса, так как это функция, которую вы передаете connect и не относится к компоненту:

class C {
  ...
}

function mapStateToProps (state) {}

connect(mapStateToProps)(C)

Наконец, вы пропустили отправку в вашем fetchContent вызов? Использует ли это Redux-Thunk?

В большинстве случаев не следует хранить все данные ответов в редукторах. Ваша иерархия состояний не должна повторять иерархию ответа JSON.

Мне бы проще было только хранить c переменная в редукторе:

//reducer1.js

export default function reducer1(state = { c: undefined }, action) {
  switch (action.type) {
    case SET_CONTENT:
      return Object.assign({}, state, { c: action.data.a.b.c });
    default:
      return state;
  }
}

Также в вашем коде есть ошибки:

  • mapStateToProps функция должна быть определена вне класса
  • Вместо прямого звонка fetchContent функция, вы должны передать его результат для отправки.
// Test.jsx

class Test extends React.Component{
  componentWillMount(){
     dispatch(fetchContent({
       type: SET_CONTENT
     }));
  }

  render() { 
    const { header } = this.props;
    return header ? (
      <div>{header}</div>
    ) : (
      <div>Loading...</div>
    );
  }
}

function mapStateToProps(state) {
  return {
    header: state.reducer1.c
  }
}

export default (mapStateToProps)(Test);
Другие вопросы по тегам