使用嵌套JSON的React / Redux mapStateToProps

时间:2017-09-27 18:49:39

标签: javascript reactjs redux

我有一个正在解析JSON的redux组件(在底部),但我无法弄清楚如何抓取嵌套的子对象。我不认为我正确理解mapStateToProps是如何工作的。

控制台日志正在转储子对象,但是当我尝试访问services.name时,我得到了

"Cannot read property 'name' of undefined"

有人可以帮我理解如何在这里映射属性吗?我已经从底部的API中抓取了一个JSON示例。

服务 - list.js

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../actions';

class ServicesList extends Component {

  componentDidMount(){
    this.props.fetchServices();
  }

  render() {
    //console.log('render called in ServicesList component');
    return (
      <table className='table table-hover'>
        <thead>
          <tr>
            <th>Service Name</th>
          </tr>
        </thead>
        <tbody>
          {this.props.services.map(this.renderServices)}
        </tbody>
      </table>
    );
  }

  renderServices(data) {
    console.log(data.services);
    const name = data.services.name;
    return(
      <tr key={name}>
        <td>{name}</td>
      </tr>
    );
  }
}

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

export default connect(mapStateToProps, actions)(ServicesList);

我的JSON看起来像这样:

{
  "services": [
     {
        "name": "redemption-service",
        "versions": {
            "desired": "20170922_145033",
            "deployed": "20170922_145033"
        },
        "version": "20170922_145033"
    }, {
        "name": "client-service",
        "versions": {
            "desired": "20170608_094954",
            "deployed": "20170608_094954"
        },
        "version": "20170608_094954"
    }, {
        "name": "content-rules-service",
        "versions": {
            "desired": "20170922_130454",
            "deployed": "20170922_130454"
        },
        "version": "20170922_130454"
    }
  ]
}

最后,我有一个公开axios.get的动作:

import axios from 'axios';

const ROOT_URL=`http://localhost:8080/services.json`;

export const FETCH_SERVICES = 'FETCH_SERVICES';

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

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

1 个答案:

答案 0 :(得分:2)

我认为您认为this.props.fetchServices()会更新services减速器,然后通过servicesmapStateToProps作为道具传递。
如果这是正确的,请注意您在componentWillMount内取货,这是一个 BIG no no。
引自componentWillMount DOCS

  

避免在此方法中引入任何副作用或订阅。

您应该在componentDidMount中获取数据。

此外,您可能认为在从ajax请求获取数据之前,不会调用render方法。你知道,反应不会等待你的ajax调用以获取数据,无论如何都会调用render方法,所以第一个render调用会尝试{{1在map的空数组上(你假设你的减速器中有一个空数组作为初始状态)。
然后,您的services函数将获得一个空数组renderServices,而data确实为data.services,因此当您尝试访问undefined时会出现错误:

  

&#34;无法读取属性&#39; name&#39;未定义&#34;

只需在渲染中使用条件:

data.services.name

修改
作为评论的后续内容,您尝试在<tbody> {this.props.services && this.props.services.map(this.renderServices)} </tbody> 上进行映射,但object在数组上工作。实际上你应该在.map而不是services.services.map(...)上进行映射,尽管你还需要检查它是否存在。
我已经使用您的代码制作了一个工作示例,我没有包含redux和ajax请求,但我使用了您正在使用的相同数据,并且我只在services.map的第二个渲染上传递它,所以它基本上与你面临的情况相同。
我甚至添加了超时来模仿延迟+添加了一个加载指示器来演示你可以(或应该)使用条件渲染做什么。

&#13;
&#13;
ServicesList
&#13;
const fakeData = {
  services: [
    {
      name: "redemption-service",
      versions: {
        desired: "20170922_145033",
        deployed: "20170922_145033"
      },
      version: "20170922_145033"
    },
    {
      name: "client-service",
      versions: {
        desired: "20170608_094954",
        deployed: "20170608_094954"
      },
      version: "20170608_094954"
    },
    {
      name: "content-rules-service",
      versions: {
        desired: "20170922_130454",
        deployed: "20170922_130454"
      },
      version: "20170922_130454"
    }
  ]
};

class ServicesList extends React.Component {
  componentDidMount() {
    this.props.fetchServices();
  }

  render() {
    const { services } = this.props;

    return (
      <table className="table table-hover">
        <thead>
          <tr>
            <th>Service Name</th>
          </tr>
        </thead>
        <tbody>
          {services.services ? (
            services.services.map(this.renderServices)
          ) : (
            this.renderLoader()
          )}
        </tbody>
      </table>
    );
  }

  renderLoader() {
    return (
      <tr>
        <td>Loading...</td>
      </tr>
    );
  }

  renderServices(data) {
    const name = data.name;
    return (
      <tr key={name}>
        <td>{name}</td>
      </tr>
    );
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: {}
    };
    this.fetchServices = this.fetchServices.bind(this);
  }

  fetchServices() {
    setTimeout(() => {
      this.setState({ data: { ...fakeData } });
    }, 1500);
  }

  render() {
    const { data } = this.state;
    return (
      <div>
        <ServicesList services={data} fetchServices={this.fetchServices} />
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
&#13;
&#13;
&#13;