为什么我的React组件渲染两次被调用,一次没有数据,然后又有数据,但是为时已晚,异常呢?

Gre*_*nce 5 javascript reactjs redux react-redux

我有一个组件TreeNav,其数据来自api调用。我已经设置了reducer / action / promise和所有管道,但是在组件渲染中,当我对数据调用map()时,得到“ Uncaught TypeError:无法读取未定义的属性'map'”。

故障排除显示TreeNav render()被调用两次。第二次是从api返回数据之后。但是由于第一个render()错误,第二个render()从未运行。

这是我的代码文件:

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

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

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

export default rootReducer;
Run Code Online (Sandbox Code Playgroud)

-------- 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;
}
Run Code Online (Sandbox Code Playgroud)

-------- actions / 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
    };
}
Run Code Online (Sandbox Code Playgroud)

-------- components / 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);
Run Code Online (Sandbox Code Playgroud)

agm*_*eod 4

就第二次渲染的错误而言,状态必须以您不期望的方式被覆盖。因此,在您的减速器中,您将返回包含任何数据的数组以及当前状态的图。使用进行连接的数组。

var a = [1,2,3]
var b = [a, ...[2,3,4]]
Run Code Online (Sandbox Code Playgroud)

编译为:

var a = [1, 2, 3];
var b = [a].concat([2, 3, 4]);
Run Code Online (Sandbox Code Playgroud)

因此,考虑到您期望有一个 Children 属性,我认为您真正想要的是一个返回对象而不是数组的减速器,并执行如下操作:

return Object.assign({}, state, { children: action.payload.data });
Run Code Online (Sandbox Code Playgroud)

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

由于您使用的是 props 而不是 state,所以去掉这一行。如果您需要管理组件内部的更改,状态会很有帮助。但您正在利用连接,因此这里不需要它。

this.state = {treedata: null};
Run Code Online (Sandbox Code Playgroud)