代码之家  ›  专栏  ›  技术社区  ›  Yanayaya

为什么我在react中接收到关于分配密钥的错误?

  •  1
  • Yanayaya  · 技术社区  · 6 年前

    我一直在努力 ReactJS demo for ASP.NET Core 我正在纠结一条错误信息:

    警告:数组或迭代器中的每个子项都应具有唯一的“键”属性。 检查的渲染方法 CommentList .有关详细信息,请参阅url。 在注释中(由commentlist创建) 在CommentList中(由CommentBox创建) 在div中(由commentbox创建) 在评论框中

    消息很清楚,数组中的每个子都需要一个密钥。代码分配了一个键,并且已经下载了Chrome I的反应控制台,也可以看到数组和添加的所有数据。

    在我的代码中,我有以下内容:

    class CommentList extends React.Component {
        render() {
            const commentNodes = this.props.data.map(comment => (
                <Comment author={comment.author} key={comment.id}>
                    {comment.author}
                </Comment>
            ));
            return (
                <div className="commentList">
                    {commentNodes}
                </div>
            );
        }
    }
    

    您可以看到键被分配给comment组件并返回到comment列表。这个 id 似乎不是空的,所以我很困惑为什么我仍然收到这个错误消息。

    你能帮我解决这个问题吗?

    这是我目前为止的完整源代码:

    js/应用程序jsx

    class CommentBox extends React.Component {
        constructor(props) {
            super(props);
            this.state = { data: [] };
            this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
        }
    
        loadCommentsFromServer() {
            const xhr = new XMLHttpRequest();
            xhr.open('get', this.props.url, true);
            xhr.onload = () => {
                const data = JSON.parse(xhr.responseText);
                this.setState({ data: data });
            };
            xhr.send();
        }
        handleCommentSubmit(comment) {
            const comments = this.state.data;
            // Optimistically set an id on the new comment. It will be replaced by an
            // id generated by the server. In a production application you would likely
            // use a more robust system for ID generation.
            comment.Id = comments.length + 1;
            const newComments = comments.concat([comment]);
            this.setState({ data: newComments });
    
            const data = new FormData();
            data.append('author', comment.author);
            data.append('text', comment.text);
    
            const xhr = new XMLHttpRequest();
            xhr.open('post', this.props.submitUrl, true);
            xhr.onload = () => this.loadCommentsFromServer();
            xhr.send(data);
        }
        componentDidMount() {
            this.loadCommentsFromServer();
            window.setInterval(() => this.loadCommentsFromServer(), this.props.pollInterval);
        }
        render() {
            return (
                <div className="commentBox card">
                    <h4>Comments</h4>
                    <CommentList data={this.state.data} />
                    <CommentForm onCommentSubmit={this.handleCommentSubmit} />
                </div>
            );
        }
    }
    class CommentList extends React.Component {
        render() {
            const commentNodes = this.props.data.map(comment => (
                <Comment author={comment.author} key={comment.id}>
                    {comment.author}
                </Comment>
            ));
            return (
                <div className="commentList">
                    {commentNodes}
                </div>
            );
        }
    }
    
    class CommentForm extends React.Component {
        constructor(props) {
            super(props);
            //Initial state?
            this.state = { author: '', text: '' };
            //Event handlers
            this.handleAuthorChange = this.handleAuthorChange.bind(this);
            this.handleTextChange = this.handleTextChange.bind(this);
            this.handleSubmit = this.handleSubmit.bind(this);
        }
        handleAuthorChange(e) {
            this.setState({ author: e.target.value });
        }
        handleTextChange(e) {
            this.setState({ text: e.target.value });
        }
        handleSubmit(e) {
            e.preventDefault();
            const author = this.state.author.trim();
            const text = this.state.text.trim();
            //If inputs are null then return nothing.
            if (!text || !author) {
                return;
            }
            //Post data to the server
            this.props.onCommentSubmit({ author: author, text: text });
            //Clear form
            this.setState({ author: '', text: '' });
        }
    
        render() {
            return (
                <div className="commentForm">
                    <form className="commentForm" onSubmit={this.handleSubmit}>
                        <input type="text" placeholder="Your name" value={this.state.author} onChange={this.handleAuthorChange} />
                        <input type="text" placeholder="Say something..." value={this.state.text} onChange={this.handleTextChange} />
                        <input type="submit" value="Post" />
                    </form>
                </div>
            );
        }
    }
    class Comment extends React.Component {
        render() {
            return (
                <div className="comment">
                    <p className="commentAuthor">
                        {this.props.author}
                    </p>
                </div>
            );
        }
    }
    
    ReactDOM.render(
        <CommentBox url="/comments" submitUrl="/comments/new" pollInterval={2000} />,
        document.getElementById('content')
    );
    

    我正在为我的数据使用一个模型,因为我稍后将向存储库介绍这个模型。

    模型/注释模型

    namespace ReactDemo.Models
    {
        public class CommentModel
        {
            public int Id { get; set; }
            public string Author { get; set; }
            public string Text { get; set; }
        }
    }
    

    控制器/家庭控制器

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using ReactDemo.Models;
    
    namespace ReactDemo.Controllers
    {
        public class HomeController : Controller
        {
            private static readonly IList<CommentModel> _comments;
            static HomeController()
            {
                _comments = new List<CommentModel>
                {
                    new CommentModel
                    {
                        Id = 1,
                        Author = "Daniel Lo Nigro",
                        Text = "Hello ReactJS.NET World!"
                    },
                    new CommentModel
                    {
                        Id = 2,
                        Author = "Pete Hunt",
                        Text = "This is one comment"
                    },
                    new CommentModel
                    {
                        Id = 3,
                        Author = "Jordan Walke",
                        Text = "This is *another* comment"
                    },
                };
            }
            public IActionResult Index()
            {
                return View();
            }
            public IActionResult Error()
            {
                return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
            }
            [Route("comments")]
            [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
            public ActionResult Comments()
            {
                return Json(_comments);
            }
            [Route("comments/new")]
            [HttpPost]
            public ActionResult AddComment(CommentModel comment)
            {
                // Create a fake ID for this comment
                comment.Id = _comments.Count + 1;
                _comments.Add(comment);
                return Content("Success :)");
            }
    
    
        }
    }
    
    4 回复  |  直到 6 年前
        1
  •  2
  •   tarzen chugh    6 年前

    您还可以使用shortid作为id的替代,这样即使您没有来自json的惟一id,key也将是惟一的。

    var shortid = require('shortid');
    function createNewTodo(text) {
      return {
        completed: false,
        id: shortid.generate(),
        text
      }
    }
    
        2
  •  2
  •   joknawe    6 年前

    使用 Id 而不是 id 对于您的键值,JavaScript是区分大小写的。同时添加 .toString()

    “id”正在此处设置:

    comment.Id = comments.length + 1;

    更新的JSX代码:

    <Comment author={comment.author} key={comment.Id.toString()}> {comment.author} </Comment> ));

    来自反应文档:

    “选择钥匙的最好方法是使用 一串 在其同级中唯一标识列表项的。最常见的情况是使用数据中的id作为密钥:“

        3
  •  1
  •   Fabian Tjoe A On    6 年前

    你的身份证可能不是唯一的。主要是不建议使用增量ID。ID在和解过程中使用,请参阅以下内容: https://reactjs.org/docs/reconciliation.html#keys

    尝试使用author作为密钥,或者更好地,尝试生成唯一的密钥(使用类似于 node-uuid 或者看到在他的答案中提供的方法。

        4
  •  0
  •   Yanayaya    6 年前

    正如这个问题上的好人所说,这个问题是由大写字母引起的 Id 被陷害 handleCommentSubmit 是的。

    handleCommentSubmit(comment) {
        comment.Id = comments.length + 1;
    }
    

    改为

    handleCommentSubmit(comment) {
        comment.id = comments.length + 1;
    }
    

    大写和小写之间的混淆是由我的模型中的值声明引起的,我已经用大写字母写了,但是,ReACTJs返回的结果当然是小写的。