使用嵌套JSON的React / Redux mapStateToProps [英] React/Redux mapStateToProps with nested JSON

查看:59
本文介绍了使用嵌套JSON的React / Redux mapStateToProps的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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. I don't think I'm understanding correctly how mapStateToProps works.

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

The console log is dumping the child objects, but when I try to access services.name I get

无法读取未定义的属性'名称'

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

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.

services-list.js

services-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如下所示:

My JSON looks like this:

{
  "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:

Finally, I have an action that exposes the axios.get here:

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


推荐答案

我认为你认为 this.props.fetchServices()将更新服务 reducer,然后将传递服务通过 mapStateToProps 作为道具。

如果这是正确的,请注意你在内取物componentWillMount ,这是一个 BIG no no。

引用来自 componentWillMount DOCS

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.
If this is correct, note that you are fetching inside componentWillMount and this is a BIG no no.
Quote from the componentWillMount DOCS:

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

Avoid introducing any side-effects or subscriptions in this method.

您应该在 componentDidMount

此外,您可能认为只有从数据库中获取数据后才会调用render方法ajax请求。你看,反应不会等待你的ajax调用以获取数据,无论如何都会调用 render 方法,所以第一个 render 调用将尝试在服务的空数组上 map (你有一个我们假设你的reducer中的空数组作为初始状态。)
然后你的 renderServices 函数将获得一个空数组 data data.services 确实未定义因此当您尝试访问 data.services.name 您收到错误:

In addition, you probably think the render method won't invoked until you got your data back from the ajax request. 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).
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:


无法读取未定义的属性'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>

修改


作为评论的后续内容,您尝试映射对象,但 .map 适用于数组。实际上你应该映射 services.services.map(...)而不是 services.map ,虽然你仍然需要检查它是否存在。

我已经用你的代码做了一个工作示例,我没有包含redux和ajax请求但是我使用了你正在使用的相同数据而我只传递它在第二次渲染 ServicesList ,所以它基本上与你面临的情况相同。

我甚至添加了一个模拟延迟的超时+添加了一个加载指示器来演示你可以(或应该)使用条件渲染做什么。

Edit
As a followup to your comment, you are trying to map on an object but .map works on arrays. so actually you should map on services.services.map(...) instead of services.map, Though you still need to check if it's exists.
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.
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>

这篇关于使用嵌套JSON的React / Redux mapStateToProps的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆