【问题标题】:Initial state undefined in redux connected react app在 redux 连接的反应应用程序中未定义初始状态
【发布时间】:2016-12-10 19:13:27
【问题描述】:

我有一个使用 Redux 管理其商店的 ReactJs 应用程序。我的状态是一个复杂的 json,它的文件可以改变。当我启动减速器时,我必须在从服务器获取内容之前访问它时指定初始状态,我得到“未定义”错误。下面是例子,

//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 会抛出 undefined,因为此时似乎没有调用 fetchContent()

所以我的问题是如何解决这个问题?在 reducer 中指定初始状态是唯一的选择吗?像下面这样,

//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;
  }
}

【问题讨论】:

    标签: javascript reactjs redux reducers


    【解决方案1】:

    可以这样做,但您也可以只更新 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 吗?

    【讨论】:

    • 因此,唯一可能的解决方案是像我一样初始化状态或对状态的每个级别的数据访问进行空检查!是的,该功能应该在外面。由于我写得很快,所以没有仔细研究。是的,fetchContent 是我的 action-creator 上的一个函数,它在内部调用 dispatch。
    • 我不确定你的期望是什么,为你做空检查?您实际上只有两种状态来检查 a) predata 和 b) 数据。这只是一个空检查,而不是“每个级别的数据访问”
    【解决方案2】:

    在大多数情况下,您不应该将所有响应数据都存储在 reducer 中。您的状态层次结构不应重复 JSON 响应的层次结构。

    在 reducer 中只存储 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);
    

    【讨论】:

    • 这是否意味着 redux-store 只能存储一个简单的 json?即使来自服务器的响应是复杂的、深度嵌套的 json ,我也应该将它们转换为简单的 1 级 json ,然后再将它们存储到 redux-store 中?另外,关于代码错误,这是一个打字错误:)我的意思是在类定义之外有mapStateToProps,而我的fetchContent是我的一个动作创建器中的一个函数,它在内部使用调度:)
    • @user3807940,视情况而定。如果您从服务器获取深度嵌套的 json,请考虑使用 normalizr 包。它用于将深层嵌套的 json 转换为 1 级数据。
    • 啊酷,以前听说过 normalizr 但从未尝试过。到时候就大喊一声!感谢您的回复:)
    • 您可以在我的项目中看到它的实际使用情况 - github.com/1ven/do
    猜你喜欢
    • 2017-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-25
    • 1970-01-01
    相关资源
    最近更新 更多