【问题标题】:Duplicate keys in React, can't solve the issue. Encountered two children with the same keyReact 中的重复键,无法解决问题。遇到两个孩子用同一个钥匙
【发布时间】:2020-04-17 12:14:03
【问题描述】:

错误:

警告:遇到两个孩子使用相同的密钥,5e0611d77833da1668feade1。密钥应该是唯一的,以便组件在更新时保持其身份。非唯一键可能会导致子项重复和/或省略 - 该行为不受支持,并且可能会在未来版本中更改。

在这张照片上 https://prnt.sc/qgfymk 我创建了 2 个博客。删除按钮工作正常。我使用 mongoose 和 MongoDB 作为我的数据库,通过 axios 向 HTTP 删除请求发送。

但是当我开始点击喜欢按钮时,检查会发生什么。 https://prnt.sc/qgg32o 它删除了我的另一篇博文,并复制了一篇具有相同名称和 ID 的博文。这里的问题是我有不同的 ID,但不知何故,当我按下 LIKE 按钮时,它给了我另一个 ID。

我会给你后端PUT请求的代码和incrementLikes的前端代码,我真的不知道发生了什么。

controllers/blogs.js(后端)

blogsRouter.put('/:id', async (request, response, next) => {

  const body = request.body
  const blogs = {
    title:body.title,
    author: body.author,
    url:body.url,
    likes: body.likes
  }

  try {
    const updatedBlog = await Blog.findOneAndUpdate(request.params.id, blogs, {
      new: true
    })
    response.json(updatedBlog.toJSON())
  } catch (exception) {
    next(exception)
  }
})

App.js

import React, { useState, useEffect } from 'react';
import './App.css';
import Blog from './components/Blog';
import LoginForm from './components/LoginForm'
import BlogForm from './components/BlogForm'
import Notification from './components/Notification'

import loginService from './services/login';
import blogService from './services/blogs';


const App = () => {

  const [blogs, setBlogs] = useState([])
  const [user, setUser] = useState(null)
  const [username, setUsername] = useState('')
  const [password, setPassword] = useState('')
  const [errorMessage, setErrorMessage] = useState(null)

  // states for blog creation
  const [title, setTitle] = useState('')
  const [author, setAuthor] = useState('')
  const [url, setUrl] = useState('')

  useEffect(() => {
    console.log('effect')
    blogService
    .getAll()
    .then(response => {
      console.log('promise fulfiled')
      setBlogs(response.data)

    })
    .catch(error => {
      console.log('response', error.response)
      console.log('error')
    })
  }, [])

  useEffect(() => {
    const loggedUserJSON = window.localStorage.getItem('loggedBlogUser')
    if (loggedUserJSON) {
      const user = JSON.parse(loggedUserJSON)
      setUser(user)
      blogService.setToken(user.token)
    }
}, [])

  //put request
  ***const incrementLike = id => {
    const blog = blogs.find(b => b.id === id)
    console.log('blog id', blog)
    const voteLike = {...blog, likes: blog.likes + 1}

    blogService
    .update(id, voteLike)
    .then(returnedBlog => {
      setBlogs(blogs.map(blog => blog.id !== id ? blog : returnedBlog))
    })
    .catch(error => {
      setErrorMessage(
        `Blog was already removed from server`
      )
      setTimeout(() => {
        setErrorMessage(null)
      }, 5000)
      })
  }***

  //login
  const handleLogin = async (e) => {
    e.preventDefault()

    try {
      const user = await loginService.login({username, password})

      window.localStorage.setItem('loggedBlogUser', JSON.stringify(user))

      setUser(user)
      setUsername('')
      setPassword('')
      console.log('success')
    } catch (exception) {
        setErrorMessage('wrong credentials')
        setTimeout(() => {
          setErrorMessage(null)
        }, 5000)
        console.log('baaad')
    }
  }

  const deleteBlogId = (id) => {
    console.log('deleted blog')
    blogService
    .del(id)
    .then(response => {
      setBlogs(blogs.filter(blog => blog.id !== id))
    })
    .catch(error => {
      console.log(error.response);
    })
  }

  const handleCreateBlog = async (e) => {
    e.preventDefault()
    const newBlogs = {
      title: title,
      author: author,
      url: url,
      date: new Date()
    }

    blogService
    .create(newBlogs)
    .then(returnedBlog => {
      setBlogs(blogs.concat(returnedBlog))
      setTitle('')
      setAuthor('')
      setUrl('')
      setErrorMessage(`${author} created new blog with name ${title}`)
      setTimeout(() => {
        setErrorMessage(null)
      }, 5000)
    })
  }


  const loginForm = () => {

    return (
      <div>
        <Notification message={errorMessage}/>
        <div>
          <LoginForm 
            username={username}
            password={password}
            handleUsernameChange={({target}) => setUsername(target.value)}
            handlePasswordChange={({target}) => setPassword(target.value)}
            handleSubmit={handleLogin}
          />
        </div>
      </div>
    )
  }


  const handleTitleChange = (event) => {
    console.log(event.target.value)
    setTitle(event.target.value)
  }


  const blogForm = () => {
    return (
      <div>
        <BlogForm
          title={title}
          author={author}
          url={url}
          handleTitleChange={handleTitleChange}
          handleAuthorChange={({target}) => setAuthor(target.value)}
          handleUrlChange={({target}) => setUrl(target.value)}
          onSubmit={handleCreateBlog}
        />

      </div>
    )
  }

  const handleLogout = async () => {
    window.localStorage.clear()
    setUser(null)
  }


  const logout = () => {
    return (
    <div><button type="reset" onClick={handleLogout}>Logout</button></div>
    )}

  const blogList = () => {
    return (
      <div>
        <h2>Blogs</h2>
        <p>{user.name} logged in</p>
        {logout()}
        {blogs.map(blog => 
        <Blog 
        key={blog.id} 
        deleteBlog={() => deleteBlogId(blog.id)}
        blog={blog} 
        increment={() => incrementLike(blog.id)} />
        )}
      </div>
    )
  }

  return (
    <div className="App">

    {user === null ?

      loginForm() :
      <div>
        <Notification message={errorMessage}/>
        {blogForm()}
        {blogList()}
      </div> 
    }
    </div>
  );
}

export default App;

检查 incrementLikes 函数。我认为有某种问题。喜欢的按钮位于名为 Blog.js 的组件中

博客.js

import React from 'react';


const Blog = ({blog, increment, deleteBlog}) => (
    <div>
        <button onClick={deleteBlog}>Delete</button> 
                {blog.title} 
                {blog.author} 
                {blog.likes} 
        <button onClick={increment}>Like</button>
    </div>
)

export default Blog

【问题讨论】:

  • 您可能想在{blogs.map(blog =&gt; 之后检查blog.id 的值

标签: javascript node.js reactjs mongoose


【解决方案1】:

虽然不应有 2 个具有相同 ID 的博客,但您可以通过将帖子的 blog.id 中的密钥替换为 index 来解决此问题。

<div>
    <h2>Blogs</h2>
    <p>{user.name} logged in</p>
    {logout()}
    //change
    {blogs.map((blog,index) => 
    <Blog 
    //change
    key={index} 
    deleteBlog={() => deleteBlogId(blog.id)}
    blog={blog} 
    increment={() => incrementLike(blog.id)} />
    )}
</div>

我在更改的行中添加了//change

【讨论】:

  • 我现在试试
  • 我认为可能仍然存在问题(可能在后端)为什么它会重复,但这应该可以解决重复键错误。
  • 这似乎与您的响应负载有关。所有ID都是唯一的吗?考虑key={${index}_${blog.id}}
  • 它解决了错误。我也可以使用 key={blod.id + Math.random()} 或类似的东西。是的,问题出在后端。我现在检查一下。
  • 没有必要把key弄复杂,它只是为了react shadow dom引用,它在任何地方都没有使用或可见。
【解决方案2】:

您可以为此使用 uuid 之类的东西,它会生成一个唯一的 ID。

import uuid from "uuid";

<>
  <h2>Blogs</h2>
  <p>{user.name} logged in</p>
  {logout()}
  {blogs.map((blog,index) => 
    <Blog 
      key={uuid.v4()} 
      deleteBlog={() => deleteBlogId(blog.id)}
      blog={blog} 
      increment={() => incrementLike(blog.id)} />
    )}
</>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 2018-10-06
    • 2021-02-22
    • 2020-01-08
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    相关资源
    最近更新 更多