React-useCallback


接下来的一段时间,我会用于写一些好用且使用频率很高的钩子,用于记录和加强对它们的使用。

一、useCallback介绍

useCallback 是一个返回记忆函数的React钩子,该函数仅在其依赖项之一发生更改时才重新创建,否则每次调用都会返回相同的函数实例。

当您有一个组件需要将函数传递给子组件或钩子函数,但又想避免每次父组件渲染时都重新创建该函数时,就是useCallback的使用场景,此方法很有用。这有助于避免不需要的组件更新周期提高应用程序的性能

二、如何使用 useCallback

一个简单的demo代入代码中

App.jsx

import { useState, useCallback } from "react";

function App() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  const reset = useCallback(() => {
    setCount(0);
  }, [count]);

  return (
    <div className="card">
      <p className="count">Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

export default App;

APP当做一个外部调用的函数、该函数使用钩子useState来管理被调用的变量的状态count

还使用了钩子useCallback: increment和创建了两个记忆函数reset

函数increment是使用useCallback和仅包含变量的依赖项列表创建的count。说明改函数只会在值count更改时重新创建。函数increment作为单击按钮处理程序,单击时它会将值递增count+1

该函数reset的创建方式类似,使用useCallback和仅包含变量 的依赖项列表count。这意味着该函数只会在值count更改时重新创建。该函数reset用作另一个按钮的点击处理程序,当点击时,它会将 的值设置count为 0。

useCallback是确保功能increment仅在需要时reset创建,防止每次渲染组件时都重新创建它们。这有助于避免不需要的组件更新周期提高应用程序的性能

三、在React中使用useCallback列出过滤器

useCallback还有一个常见的用途之一是有一个列表并且想要对该列表进行即时搜索的情况下使用

App.jsx

import "./App.css";
import { useState, useCallback } from "react";

function App() {
  const [users, setUsers] = useState([
    { id: 1, name: "张三", image: "https://xxx.jpg" },
    { id: 2, name: "李四", image: "https://xxx.jpg" },
    { id: 3, name: "王五", image: "https://xxx.jpg" },
    { id: 4, name: "赵六", image: "https://xxx.jpg" },
    { id: 5, name: "小明", image: "https://xxx.jpg" },
    { id: 6, name: "小红", image: "https://xxx.jpg" },
    { id: 7, name: "小刚", image: "https://xxx.jpg" },
  ]);
  const [filter, setFilter] = useState("");

  const handleFilterChange = useCallback((event) => {
    setFilter(event.target.value);
  }, [filter]);

  const filteredUsers = useCallback(() => {
    return users.filter((user) =>
      user.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [users, filter]);

  return (
    <div className="parent">
      <input value={filter} onChange={handleFilterChange} placeholder="请输入搜索" />
      <button onClick={() => setFilter("")}>搜索</button>
      <p className="text">搜索描述</p>
      <ChildComponent users={filteredUsers} />
    </div>
  );
}

function ChildComponent({ users }) {
  return (
    <ul className="child">
      {users().map((user) => (
        <li key={user.id}>
          <img src={user.image} alt={user.name} />
          <p>{user.name}</p>
        </li>
      ))}
    </ul>
  );
}

export default App;
  1. 第一次调用列表 useState初始化状态变量users并返回次列表和函数setUsers
  2. useState再次调用以filter用空字符串初始化状态变量并返回该字符串和函数setFilter
  3. 函数handleFilterChange是使用创建的,并且只有在值更改useCallback时才会重新创建。filter该函数handleFilterChange被称为输入事件处理程序onChange,并使用输入值更新 的filter值。
  4. useCallback再次调用以创建函数filteredUsers,该函数根据 的值过滤(用户)列表filter。仅当或的值发生变化filteredUsers时才会重新创建该函数。users``filter
  5. 该组件App呈现搜索条目、过滤器重置按钮和子组件ChildComponent。的当前值filter作为查找输入值handleFilterChange传递,函数作为事件处理程序传递onChange。子组件作为属性传递给函数filteredUsers,用于呈现过滤后的卡片列表。

四、useCallback用于避免在React组件中重新创建函数

React hook允许以不可变useCallback的方式返回一个函数,避免在每次渲染父组件时重新创建它。这在将函数作为属性传递给子组件的情况下非常有用,并且可以提高应用程序性能

App.jsx

import { useState, useCallback } from "react";

function App() {
  const [selectedType, setSelectedType] = useState(null);
  const [pokemons, setPokemons] = useState([
    { id: 1, name: "Abra", type: "psychic" },
    { id: 2, name: "Bellsprout", type: "grass" },
    { id: 3, name: "Chansey", type: "normal" },
  ]);

  const handleSelectType = useCallback((type) => {
    setSelectedType(type);
  }, [selectedType]);

  return (
    <div className="parent">
      <ul className="child">
        {pokemons.map((pokemon) => (
          <li key={pokemon.id}>
            <img src={`https://img.pokemondb.net/artwork/${pokemon.name.toLowerCase()}.jpg`} alt={pokemon.name} />
            <button onClick={() => handleSelectType(pokemon.type)}>
              {pokemon.name}
            </button>
          </li>
        ))}
      </ul>
      {selectedType && (
        <DetailsComponent selectedType={selectedType} />
      )}
    </div>
  );
}

function DetailsComponent({ selectedType }) {
  return <div className="type">这是类型: {selectedType}</div>;
}

export default App;
  1. 创建一个名为handleSelectTypeusing的回调函数useCallback。该函数使用传递给它selectedType的参数更新状态。type的第二个参数useCallback是一个依赖项数组,在本例中只是 的值selectedType。这意味着如果 的值selectedType没有改变,handleSelectType则返回相同的实例并且可以在不重新渲染组件的情况下重复使用。
  2. 返回 JSX,它呈现 Pokemon 列表和一个组件(DetailsComponent如果它selectedType有值)。
  3. DetailsComponent接收组件定义selectedType为一个变量。
  4. 最终呈现 这是类型 xxx

小结

useCallback上面的代码用于创建一个被调用的回调函数handleSelectType,该函数作为 prop 传递给子组件。

当传递给的参数useCallback(在这种情况下,只是 的值selectedType)不变时,将handleSelectType返回相同的实例,子组件可以重用它而无需重新渲染。这对于防止子组件在回调函数未更改时被不必要地渲染。

五、总结

React hookuseCallback是一个有用的工具,可以避免每次渲染组件时都重新创建函数,从而保证更好的应用程序性能。

此外,通过返回不可变函数,useCallback确保作为属性传递给子组件的函数不会随着父组件的每次渲染而改变,这在确保函数行为的一致性,在某些特定的情况下使用是很有必要的。


文章作者: feico
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 feico !
评论
  目录