React官网-你可能不需要effect-文末挑战使用key重新渲染组件为什么不能直接写在内部标签上?

React官方文档中的一道挑战题:

https://zh-hans.react.dev/learn/you-might-not-need-an-effect#...

官方答案是提取出一个EditForm组件,为其加上key

import { useState, useEffect } from 'react';

export default function EditContact({ savedContact, onSave }) {
  return (
    <EditForm
      savedContact={savedContact}
      onSave={onSave}
      key={savedContact.id}
      ></EditForm>
  )
  }
function EditForm({ savedContact, onSave }) {
  const [name, setName] = useState(savedContact.name);
  const [email, setEmail] = useState(savedContact.email);

  return (
    <section>
      <label>
        姓名:{' '}
        <input
          type="text"
          value={name}
          onChange={e => setName(e.target.value)}
        />
      </label>
      <label>
        邮箱:{' '}
        <input
          type="email"
          value={email}
          onChange={e => setEmail(e.target.value)}
        />
      </label>
      <button onClick={() => {
        const updatedData = {
          id: savedContact.id,
          name: name,
          email: email
        };
        onSave(updatedData);
      }}>
        保存
      </button>
      <button onClick={() => {
        setName(savedContact.name);
        setEmail(savedContact.email);
      }}>
        重置
      </button>
    </section>
  );
}

我想知道,为什么不能直接在组件内的<section>上加key?

import { useState, useEffect } from 'react';

export default function EditContact({ savedContact, onSave }) {
  const [name, setName] = useState(savedContact.name);
  const [email, setEmail] = useState(savedContact.email);

  return (
    <section key={savedContact.id}>
      <label>
        姓名:{' '}
        <input
          type="text"
          value={name}
          onChange={e => setName(e.target.value)}
        />
      </label>
      <label>
        邮箱:{' '}
        <input
          type="email"
          value={email}
          onChange={e => setEmail(e.target.value)}
        />
      </label>
      <button onClick={() => {
        const updatedData = {
          id: savedContact.id,
          name: name,
          email: email
        };
        onSave(updatedData);
      }}>
        保存
      </button>
      <button onClick={() => {
        setName(savedContact.name);
        setEmail(savedContact.email);
      }}>
        重置
      </button>
    </section>
  );
}

我是新手,刚学React,我用过Vue。我无法理解这里的做法。

阅读 3.3k
2 个回答

key不一样代表这是一个"新"的组件实例,它会重新创建,也就是重新经历vue中的created->mounted的过程即重新初始化实例,那么EditForm上持有的那些state(name/email)就会重置,而如果你key绑在section上那EditForm就不会重置自身的state,这一点在vue中也是一样的,你更改组件的key,那么组件上原先内部修改的那些data都会被重置掉

  const [name, setName] = useState(savedContact.name);
  const [email, setEmail] = useState(savedContact.email);

第一次调用(初次创建了一个实体)这个useState的时候,返回的name就是savedContact.nameemail就是savedContact.email,但是之后再调用name, email就和savedContact.name,savedContact.email没有关系了,也就是说即使你切换到了另一个tab上,savedContact改变了,也没有变化,它需要借助setName, setEmail来改变它。

那怎么才能在切换tab的时候更新展示的内容呢

方法一、虽然name,email没有改变,但是此时的savedContact变了,重新给name,email赋值一下并触发重新渲染就可以了。

  useEffect(() => {
    setName(savedContact.name);
    setEmail(savedContact.email);
  }, [savedContact]);

方法二、在每次切换tab的时候,产生新的第一次调用(也就说每次产生一个新的实体),此时的useState就返回当前tab对应的contact信息了。你在section上添加key,虽然会在切换的时候产生新的section实体,但是并不影响useState所在的实体,它还是不变的,它依然返回的是第一次调用时传入的savedContact,或你setName,setEmail更改后的。

其实你在App.js文件上直接给EditContact加上key也是一样的。

<EditContact 
    savedContact={selectedContact} 
    onSave={handleSave} 
    id={selectedContact.id}
/>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题