反应-警告:列表中的每个孩子都应该有一个唯一的“关键”道具

时间:2019-11-04 21:03:15

标签: reactjs

在这个简单的React App中,我不明白为什么收到以下警告消息:

  

警告:列表中的每个孩子都应该有一个唯一的“关键”道具。

在我看来,我似乎以 key = {item.login.uuid}

的形式将密钥放在正确的位置

如何摆脱警告消息? 放置钥匙的正确位置在哪里?

App.js

import UserList from './List'

const App = props => {
  const [id, newID] = useState(null)
  return (
    <>
      <UserList id={id} setID={newID} />
    </>
  )
}

export default App

List.js

const UserList = ({ id, setID }) => {
  const [resources, setResources] = useState([])

  const fetchResource = async () => {
    const response = await axios.get(
      'https://api.randomuser.me'
    )
    setResources(response.data.results)
  }

  useEffect(() => {
    fetchResource()
  }, [])

  const renderItem = (item, newID) => {

    return (
      <>
        {newID ? (
          // User view
          <div key={item.login.uuid}>
            <div>
              <h2>
                {item.name.first} {item.name.last}
              </h2>
              <p>
                {item.phone}
                <br />
                {item.email}
              </p>
              <button onClick={() => setID(null)}>
                Back to the list
              </button>
            </div>
          </div>
        ) : (
          // List view
          <li key={item.login.uuid}>
            <div>
              <h2>
                {item.name.first} {item.name.last}
              </h2>
              <button onClick={() => setID(item.login.uuid)}>
                Details
              </button>
            </div>
          </li>
        )}
      </>
    )
  }

  const user = resources.find(user => user.login.uuid === id)

  if (user) {
    // User view
    return <div>{renderItem(user, true)}</div>
  } else {
    // List view
    return (
      <ul>
        {resources.map(user => renderItem(user, false))}
      </ul>
    )
  }
}

export default UserList

2 个答案:

答案 0 :(得分:2)

key必须位于循环内的根级元素上。您的情况就是片段(<>)。

要做到这一点,您需要将其完整写出:

const renderItem = (item, newID) => {

  return (
    <Fragment key={item.login.uuid}>
      {newID ? (
        ...
      )}
    </Fragment>
  );
}

(您可以将Fragment添加到react的其他导入中)。

请注意,您的示例中实际上并不需要该片段,您可以将其删除并将key保留在它们所在的位置,因为之后<div><li>将成为根元素:

const renderItem = (item, newId) => {

  return newID ? (
    <div key={item.login.uuid}>
      ...
    </div>
  ) : (
    <li  key={item.login.uuid}>
      ...
    </li>
  )
}

答案 1 :(得分:1)

如果您创建2个单独的组件,一个用于用户视图,一个用于列表项,该怎么办。这样,您只需要传递用户属性即可。另外,使用JSX并从那里传递wht键。

const UserList = ({ id, setID }) => {
  const [resources, setResources] = useState([])

  const fetchResource = async () => {
    const response = await axios.get(
      'https://api.randomuser.me'
    )
    setResources(response.data.results)
  }

  useEffect(() => {
    fetchResource()
  }, [])

  const User = ({user}) => (
    <div key={user.login.uuid}>
      <div>
        <h2>
          {user.name.first} {user.name.last}
        </h2>
        <p>
          {user.phone}
          <br />
          {user.email}
        </p>
        <button onClick={() => setID(null)}>
          Back to the list
        </button>
      </div>
    </div>
  )

  const ListItem = ({user}) => (
    <li key={user.login.uuid}>
      <div>
        <h2>
          {user.name.first} {user.name.last}
        </h2>
        <button onClick={() => setID(user.login.uuid)}>
          Details
        </button>
      </div>
    </li>
  )

  const user = resources.find(user => user.login.uuid === id)

  if (user) {
    // User view
    return <User user={user}</div>
  } else {
    // List view
    return (
      <ul>
        {resources.map((user, index) => <ListItem key={index} user={user} />)}
      </ul>
    )
  }
}

export default UserList