【问题标题】:Why is my React component render called twice, once without data and then later with data, but too late exception?为什么我的 React 组件渲染调用了两次,一次没有数据,然后一次有数据,但为时已晚异常?
【发布时间】:2016-08-21 15:56:47
【问题描述】:

我有一个组件 TreeNav,其数据来自 api 调用。我已经设置了 reducer/action/promise 和所有的管道,但是当我在数据上调用 map() 时,在组件渲染中,得到“Uncaught TypeError: Cannot read property 'map' of undefined”。

故障排除显示 TreeNav render() 被调用了两次。第二次是在数据从 api 返回之后。但由于第一次渲染()错误,第二次渲染()永远不会运行。

这是我的代码文件:

-------- reducers/index.js ---------

import { combineReducers } from 'redux';
import TreeDataReducer from './reducer_treedata';

const rootReducer = combineReducers({
  treedata: TreeDataReducer
});

export default rootReducer;

-------- reducers/reducer_treedata.js ---------

import {FETCH_TREE_DATA} from '../actions/index';

export default function (state=[], action) {
    switch (action.type) {
        case FETCH_TREE_DATA: {
            return [action.payload.data, ...state];
        }
    }

    return state;
}

-------- 动作/index.js --------

import axios from 'axios';

const ROOT_URL = 'http://localhost:8080/api';

export const FETCH_TREE_DATA = 'FETCH_TREE_DATA';

export function fetchTreeData () {
    const url = `${ROOT_URL}/treedata`;
    const request = axios.get(url);

    return {
        type: FETCH_TREE_DATA,
        payload: request
    };
}

-------- 组件/tree_nav.js --------

import React, {Component} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {fetchTreeData} from '../actions/index';

class TreeNav extends Component {
  constructor (props) {
    super(props);

    this.state = {treedata: null};
    this.getTreeData();             
  }

  getTreeData () {
    this.props.fetchTreeData();
  }

  renderTreeData (treeNodeData) {
    const text = treeNodeData.text;

    return (
      <div>
        {text}
      </div>
    );
  }

  render () {
    return (
      <div className="tree-nav">
        {this.props.treedata.children.map(this.renderTreeData)}   
      </div>
    );
  }
}

function mapStateToProps ({treedata}) {
    return {treedata};
}

// anything returned from this function will end up as props 
// on the tree nav
function mapDispatchToProps (dispatch) {
  // whenever selectBook is called the result should be passed to all our reducers
  return bindActionCreators({fetchTreeData}, dispatch);
}

// Promote tree_nav from a component to a container. Needs to know about
// this new dispatch method, fetchTreeData. Make it available as a prop.
export default connect(mapStateToProps, mapDispatchToProps)(TreeNav);

【问题讨论】:

  • 该操作不应该在axios.get().then()回调中吗?并且有效负载是响应而不是请求。
  • 忘记我之前的问题,我想你正在使用promise中间件。你是吗?
  • 是的,使用 promise 中间件,而且我对 react/redux 还很陌生,我可能不知道何时/如何以其他方式进行操作。使用从 Udemy 课程中获得的代码。

标签: javascript reactjs redux react-redux


【解决方案1】:

就您第二次渲染的错误而言,状态必须以您未预料到的方式被覆盖。因此,在您的 reducer 中,您将返回包含任何数据的数组,以及当前状态的 splat。使用执行 concat 的数组。

var a = [1,2,3]
var b = [a, ...[2,3,4]]

编译为:

var a = [1, 2, 3];
var b = [a].concat([2, 3, 4]);

所以考虑到你期望一个 children 属性,我认为你真正想要的是一个返回对象而不是数组的 reducer,而是做这样的事情:

return Object.assign({}, state, { children: action.payload.data });

从那里确保将初始状态更新为一个带有空子数组的对象。

摆脱这一行,因为您使用的是道具而​​不是状态。如果您只需要在组件内部管理更改,则状态会很有帮助。但是您正在利用连接,所以这里不需要。

this.state = {treedata: null};

【讨论】:

    【解决方案2】:

    通过检查 this.props.treedata 等的存在解决了这个问题,如果还没有,就应该 div “正在加载...”。

      render () {
        if (this.props.treedata && this.props.treedata[0] && this.props.treedata[0].children) {
          console.dir(this.props.treedata[0].children);
          return (
            <div className="tree-nav">
              {this.props.treedata[0].children.map(this.renderTreeData)}   
            </div>
          );
        } else {
          return <div>Loading...</div>;
        }
      }
    

    【讨论】:

    • 这个答案很好,但我建议让 reducer 更聪明地处理它构建的数据对象。只是让你的视图代码更干净:)
    • 我目前正在使用此检查。但我想知道是否有更好的解决方案,渲染不必运行两次。有更新吗?
    猜你喜欢
    • 2020-09-07
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 2020-08-25
    • 2020-11-22
    相关资源
    最近更新 更多