译:101个React技巧#3Keys&Refs

256 阅读2分钟

20. 使用 crypto.randomUUIDMath.random 生成 keys

JSX 元素在 map() 调用中总是需要 keys。

如果你的元素还没有 keys,可以使用 crypto.randomUUIDMath.randomuuid 库生成唯一 ID。

注意:crypto.randomUUID 在旧浏览器中未定义。

21. 确保列表项的 IDs 是稳定的(即在重新渲染之间不会改变)

尽可能保持 keys/IDs 的稳定性。

否则,React 可能会无用地重新渲染某些组件,或者选择将不再有效,如下例所示。

❌ 不好: selectedQuoteIdApp 渲染时改变,所以永远不会有有效的选择。

function App() {
  const [quotes, setQuotes] = useState([]);
  const [selectedQuoteId, setSelectedQuoteId] = useState(undefined);

  // 获取 quotes
  useEffect(() => {
    const loadQuotes = () =>
      fetchQuotes().then((result) => {
        setQuotes(result);
      });
    loadQuotes();
  }, []);

  // 添加 ids: 这样不好!!!
  const quotesWithIds = quotes.map((quote) => ({
    value: quote,
    id: crypto.randomUUID(),
  }));

  return (
    <List
      items={quotesWithIds}
      selectedItemId={selectedQuoteId}
      onSelectItem={setSelectedQuoteId}
    />
  );
}

✅ 好: 在获取 quotes 时就添加 IDs

function App() {
  const [quotes, setQuotes] = useState([]);
  const [selectedQuoteId, setSelectedQuoteId] = useState(undefined);

  // 获取 quotes 并保存带 ID
  useEffect(() => {
    const loadQuotes = () =>
      fetchQuotes().then((result) => {
        // 我们一得到结果就添加 `ids`
        setQuotes(
          result.map((quote) => ({
            value: quote,
            id: crypto.randomUUID(),
          }))
        );
      });
    loadQuotes();
  }, []);

  return (
    <List
      items={quotes}
      selectedItemId={selectedQuoteId}
      onSelectItem={setSelectedQuoteId}
    />
  );
}

22. 策略性地使用 key 属性来触发组件重新渲染

想要强制组件从头开始重新渲染?只需改变它的 key

在下面的例子中,我们使用这个技巧在切换到新标签时重置错误边界。

🏖 沙盒

23. 使用 ref callback 函数 来执行诸如监视大小变化和管理多个节点元素等任务。

你知道你可以传递一个函数给 ref 属性而不是 ref 对象吗?

工作原理:

  • 当 DOM 节点添加到屏幕时,React 会调用该函数并将 DOM 节点作为参数。
  • 当 DOM 节点被移除时,React 会调用该函数并传入 null

在下面的例子中,我们使用这个技巧来跳过 useEffect

❌ 之前: 使用 useEffect 来聚焦输入

function App() {
  const ref = useRef();

  useEffect(() => {
    ref.current?.focus();
  }, []);

  return <input ref={ref} type="text" />;
}

✅ 之后: 在输入可用时立即聚焦

function App() {
  const ref = useCallback((inputNode) => {
    inputNode?.focus();
  }, []);

  return <input ref={ref} type="text" />;
}