简体   繁体   English

使用嵌套JSON的React / Redux mapStateToProps

[英]React/Redux mapStateToProps with nested JSON

I have a redux component that is parsing the JSON (at bottom), but I can't figure out how to grab the nested child objects. 我有一个解析JSON的redux组件(在底部),但我无法弄清楚如何获取嵌套的子对象。 I don't think I'm understanding correctly how mapStateToProps works. 我不认为我正确理解mapStateToProps是如何工作的。

The console log is dumping the child objects, but when I try to access services.name I get 控制台日志正在转储子对象,但是当我尝试访问services.name时,我得到了

"Cannot read property 'name' of undefined"

Can someone help me understand how to map properties here? 有人可以帮我理解如何在这里映射属性吗? I've included an example of the JSON I'm grabbing from the API at the bottom. 我已经包含了一个我从底部的API中获取的JSON示例。

services-list.js 服务-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);

My JSON looks like this: 我的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"
    }
  ]
}

Finally, I have an action that exposes the axios.get here: 最后,我有一个公开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
  };
}

I assume that you think this.props.fetchServices() will update the services reducer and then will pass the services as a prop via the mapStateToProps . 我假设您认为this.props.fetchServices()将更新services reducer,然后通过mapStateToPropsservices作为prop mapStateToProps
If this is correct, note that you are fetching inside componentWillMount and this is a BIG no no. 如果这是正确的,请注意你在componentWillMount中获取,这是一个很大的不。
Quote from the componentWillMount DOCS : 来自componentWillMount DOCS的报价:

Avoid introducing any side-effects or subscriptions in this method. 避免在此方法中引入任何副作用或订阅。

You should fetch data in componentDidMount . 您应该在componentDidMount获取数据。

In addition, you probably think the render method won't invoked until you got your data back from the ajax request. 此外,您可能认为在从ajax请求获取数据之前不会调用render方法。 You see, react won't wait for your ajax call to get back with the data, the render method will be invoked no matter what, so the first render call will try to map on an empty array of services (you got an empty array as initial state in your reducer i assume). 你看,反应将不会等待你的ajax调用以获取数据,无论如何都会调用render方法,所以第一个render调用将尝试map一个空的services数组(你得到一个空数组作为你的减速器的初始状态我假设)。
Then your renderServices function will get an empty array as data and data.services is indeed undefined hence when you try to access data.services.name you get the error: 然后你的renderServices函数将得到一个空数组,因为datadata.services确实是undefined因此当你尝试访问data.services.name时会出现错误:

"Cannot read property 'name' of undefined" “无法读取未定义的属性'名称'

Just use a condition in your render: 只需在渲染中使用条件:

<tbody>
  {this.props.services && this.props.services.map(this.renderServices)}
</tbody>

Edit 编辑
As a followup to your comment, you are trying to map on an object but .map works on arrays. 作为评论的后续内容,您尝试映射object.map适用于数组。 so actually you should map on services.services.map(...) instead of services.map , Though you still need to check if it's exists. 实际上你应该映射services.services.map(...)而不是services.map ,虽然你仍然需要检查它是否存在。
I've made a working example with your code, i did not include redux and ajax requests but i used the same data you are using and i'm passing it only on the second render of ServicesList , so it basically has the same scenario you are facing. 我已经用你的代码做了一个工作示例,我没有包含redux和ajax请求,但我使用的是你正在使用的相同数据,我只在第二个ServicesList上传递它,所以它基本上有相同的场景你面对。
I've even added a timeout to mimic a delay + added a loading indicator to demonstrate what you can (or should) do with conditional rendering. 我甚至添加了一个模拟延迟的超时+添加了一个加载指示器来演示你可以(或应该)使用条件渲染做什么。

 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")); 
 <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM