如何避免 reactjs 中的 key/id 问题并使 props 从 parent 传递到 child?

how to avoid key/id problems in reactjs and make props pass from parent to child?

我在尝试将 parent 数据传递给 child 组件时总是碰壁。

我的看法:

<%= react_component 'Items', { data: @items } %>

我的 Items 组件进行 ajax 调用、设置状态并呈现 Item。将 key={this.props.id} 排除在传递给映射函数的 Item 实例之外,以便组件 html 呈现到页面。但是添加密钥,我得到一个控制台错误:Uncaught TypeError: Cannot read property 'id' of undefined

这是'Items':

var Items = React.createClass({
    loadItemsFromServer: function() {
        $.ajax({
            url: this.props.url,
            dataType: 'json',
            cache: false,
            success: function(data) {
                this.setState({data: data});
            }.bind(this),
            error: function(xhr, status, err) {
                console.error(this.props.url, status, err.toString());
            }.bind(this)
        });
    },
    componentDidMount: function() {
        this.loadItemsFromServer();
    },
    render: function() {
        var itemNodes = this.props.data.map(function() {
            return (
                <Item key={this.props.id} />
            );
        });
        return (
            <div className="ui four column doubling stackable grid">
                {itemNodes}
            </div>
        );
    }
});

我的 item.js.jsx 组件只是格式化每个 Item:

var Item = React.createClass({
    render: function() {
        return (
            <div className="item-card">
                <div className="image">

                </div>
                <div className="description">
                    <div className="artist">{this.props.artist}</div>
                </div>
            </div>
        );
    }
});

React 开发工具扩展显示项目内的道具和状态数据。然而child仁是空的。

我知道 this,但我正在使用 this.props.id 设置密钥。我不确定我错过了什么?

您的实施存在一些问题。

首先,您需要决定:是否要从您的视图中呈现传递给 Items 组件的 @items?或者你想异步加载它们? 因为现在我得到的印象是你正在尝试两者都做...

呈现从视图传递的项目

如果您想呈现传递给组件的视图中的项目,请确保它是正确的 json。您可能需要对其调用 'as_json'。

<%= react_component 'Items', { data: @items.as_json } %>

然后,在您的组件中,映射项目以呈现 <Item /> 组件。这是第二个问题,关于你的钥匙。您需要将 item 变量定义到您的 map 函数的回调函数,并从中读取 id:

var itemNodes = this.props.data.map(function(item) {
  return (
    <Item key={item.id} artist={item.artist} />
  );
});

请注意,我还将作者添加为 prop,因为您在 <Item /> 组件中使用了它。

您可以删除 componentDidMountloadItemsFromServer 函数,因为您没有使用它们。

异步加载项目

如果您想异步加载项目,就像您在 loadItemsFromServer 函数中尝试做的那样,首先,从您的视图中传递 url 并删除 {data: @ items} 部分,因为您将从组件加载项目,例如:

<%= react_component 'Items', { url: items_path(format: :json) } %>

如果要呈现异步获取的项目,请使用:

var itemNodes = this.state.data.map(function(item) {
  return (
    <Item key={item.id} artist={item.artist} />
  );loadItemsFromServer
});

请注意,我将 this.props.map 更改为 this.state.map

您现在可以使用 componentDidMount 和 loadItemsFromServer 函数加载数据并将它们保存到状态。

我在 Items 组件

中发现您发布的代码存在一些问题
  1. 您正在呈现 this.props.data,而实际上 this.state.data 是使用 ajax 请求更新的那个。您需要渲染 this.state.data 但从 props
  2. 获取初始值
  3. map 迭代器函数接受一个表示当前数组元素的参数,使用它来访问属性而不是使用未定义的 this

更新后的代码应该如下所示

var Item = React.createClass({
    render: function() {
        return (
            <div className="item-card">
                <div className="image">

                </div>
                <div className="description">
                    <div className="artist">{this.props.artist}</div>
                </div>
            </div>
        );
    }
});

var Items = React.createClass({
    getInitialState: function() {
        return {
            // for initial state use the array passed as props,
            // or empty array if not passed
            data: this.props.data || []
        };
    },
    loadItemsFromServer: function() {
        var data = [{
            id: 1,
            artist: 'abc'
        }, {
            id: 2,
            artist: 'def'
        }]
        this.setState({
            data: data
        });

    },
    componentDidMount: function() {
        this.loadItemsFromServer();
    },
    render: function() {
        // use this.state.data not this.props.data, 
        // since you are updating the state with the result of the ajax request, 
        // you're not updating the props
        var itemNodes = this.state.data.map(function(item) {
            // the map iterator  function takes an item as a parameter, 
            // which is the current element of the array (this.state.data), 
            // use (item) to access properties, not (this)

            return (
                // use key as item id, and pass all the item properties 
                // to the Item component with ES6 object spread syntax
                <Item key={item.id} {...item} />
            );
        });
        return (
            <div className="ui four column doubling stackable grid">
                {itemNodes}
            </div>
        );
    }
});

这是一个工作示例http://codepen.io/Gaafar/pen/EyyGPR?editors=0010