反应使用回调

 Dead Simple Chat 允许您使用强大的Javascript Chat SDK轻松地将 Chat 添加到任何 React 应用程序

在这篇博文中,我们将看到如何使用 React  useCallback hook。 

它的目的是什么,我们还将研究应该使用它的真实场景以及使用 React 时要避免的常见陷阱 useCallback

什么是 useCallback?

useCallback 用于缓存函数“定义”。它通常与React memo结合使用 。 

如果你已经使用 React memo 缓存了你的组件,那么它不会重新渲染,除非它的 props 被改变。  

如果您向组件传递一个函数,那么您的函数将每次都重新渲染,从而违背了使用 memo 的目的。

因为在 Javascript 中 function() {} 还是 () => { } 创建了不同的函数,因此 prop 永远不会相同,导致组件重新渲染,make memo 无用。

为防止这种情况发生,您可以将函数定义包装在里面 useCallback ,这样可以防止重新创建函数,除非依赖关系发生变化。

的语法 useCallback

该 useCallback 钩子接受两个参数,一个是你想要缓存的方法/函数,第二个参数是依赖数组,它返回缓存的函数。

当依赖数组中传递的变量发生变化时, useCallback 钩子返回并更新函数。

const method = useCallback(<METHOD>, [<DEPENDENCY_ARRAY]);

让我们看一些例子来更好地理解它。

Dead Simple Chat 允许您使用功能强大的 Javascript Chat SDK 在几分钟内将聊天集成到您的 React 或 Web 应用程序中 。

的例子 useCallback

考虑以下代码:

import React, { useState } from "react";

const ChildComponent = React.memo(({ onButtonClick }) => {
  console.log("ChildComponent rendered");
  return <button onClick={onButtonClick}>Increment</button>;
});

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

  const [theme, setTheme] = useState("light");

  const handleButtonClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h1>Current Theme: {theme}</h1>
      <button
        onClick={() => {
          theme === "light" ? setTheme("dark") : setTheme("light");
        }}
      >
        Toggle Theme
      </button>
      <h1>Counter: {count}</h1>
      <ChildComponent onButtonClick={handleButtonClick} />
    </div>
  );
}

export default ParentComponent;

在上面的代码中,我们创建了一个 ChildComponent 并使用 React. memo.

React 备忘录的简要介绍:

 React.memo 除非更改道具,否则使用组件不会重新渲染。

通常,当重新渲染父组件时,所有子组件也会重新渲染。

但是通过在子组件上使用 React memo,当父组件被重新渲染时,子组件不会被重新渲染,除非子组件的 props 发生变化。

在中, ChildComponent 我们还添加了一条 console.log 语句以在控制台上打印“ChildComponent rendered”。

我们正在接受一个方法 onButtonClick 作为 的道具 ,并 在按下按钮时ChildComponent 调用该事件的方法 。onClick

接下来,我们创建了一个 ParentComponent 并在其中 ParentComponent创建了两个状态变量,一个是 count ,另一个是 theme

在中, ParentComponent 我们创建了一个名为 as 的方法 handleButtonClick ,它会增加我们的状态变量 count ,我们将此方法作为 prop 传递给 ChildComponent.

我们还创建了一个按钮来切换称为 the 的第二个状态变量, theme 当按下按钮时,我们将主题从浅色切换到深色。

我们还显示当前主题和计数。

当我们使用 React.memo 时,我们预期的行为是当我们切换主题时,我们不应该 ChildComponent rendered在屏幕上看到消息。

让我们试试看:

正如您在上面的视频中看到的那样,每次按下“切换主题”按钮时,屏幕上都会打印“已呈现子组件”消息。

为什么会这样? 

我们在 React useCallback 介绍中讨论过,它的发生是因为 JavaScript 每次渲染组件时都会创建一个新函数。

我们将函数作为 prop 传递,React memo 会将其视为新函数并重新渲染子组件。

为了解决这个问题,我们将包装我们的函数 useCallback ,它将返回我们函数的缓存版本。

这是更新后的代码:

import React, { useState, useCallback } from "react";

const ChildComponent = React.memo(({ onButtonClick }) => {
  console.log("ChildComponent rendered");
  return <button onClick={onButtonClick}>Click me</button>;
});

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

  const [theme, setTheme] = useState("light");

  // Using useCallback to cache the function
  const handleButtonClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <h1>Current Theme: {theme}</h1>
      <button
        onClick={() => {
          theme === "light" ? setTheme("dark") : setTheme("light");
        }}
      >
        Toggle Theme
      </button>
      <h1>Counter: {count}</h1>
      <ChildComponent onButtonClick={handleButtonClick} />
    </div>
  );
}

export default ParentComponent;

在我们更新的代码中,我们将函数包装在 useCallback钩子中。让我们看看更新后的代码如何执行:

正如您在控制台中看到的那样,每次我们按下“切换主题”按钮时,都不会打印“ChildComponent rendered”消息。

让我们看看在哪些地方使用 useCallback.

真实场景 useCallback

我们将查看一些带有代码示例的真实场景,在这些场景中使用 using useCallback 会很有用。

无限滚动中的 Memoize 数据获取函数

在无限滚动列表中,我们可以使用 来 useCallback 缓存负责获取数据的函数,以防止不必要的渲染和 API 调用。

让我们看一个示例,我们将使用免费提供的 Github List Users API 构建一个简单的无限滚动列表。

在本例中,我们将  结合使用useCallback 和 来防止不必要的 API 调用。useEffect

import { useState, useEffect, useCallback } from "react";

function InfinitUserScroll() {
  const [users, setUsers] = useState([]);
  const [page, setPage] = useState(1);

  const fetchData = useCallback(async () => {
    const response = await fetch(
      `https://api.github.com/users?since=${page * 30}`
    );
    const nextData = await response.json();
    setUsers((curData) => [...curData, ...nextData]);
  }, [page]);

  useEffect(() => {
    fetchData();
  }, [fetchData]);

  const handleScroll = (e) => {
    const { scrollTop, scrollHeight, clientHeight } = e.target;
    if (scrollHeight - scrollTop === clientHeight) {
      setPage((prevPage) => prevPage + 1);
    }
  }

  return (
    <>
      <div
        onScroll={handleScroll}
        style={{ overflowY: "scroll", height: "400px" }}
      >
        <h1>Github Users</h1>
        <hr />
        {users.map((item, index) => (
          <div key={index}>{item.login}</div>
        ))}
      </div>
    </>
  );
}

export default InfinitUserScroll;

在上面的代码中,我们用来 useCallback 缓存方法的一个是 fetchData 方法。

该 fetchData 方法被缓存, page 状态变量是传递给缓存 fetchData 方法的 useCallback 的依赖项。

然后我们 fetchData 作为依赖项传递 useEffect 并调用 fetchData 方法 useEffect

列为 page 依赖项 useCallback 导致 fetchData仅当值更改时才重新创建方法 page 。

当 值更改时, 将创建 page 新 方法,这会导致 触发以获取新页面信息。fetchDatauseEffect

在更新页面值之前, fetchData 不会调用该方法,从而防止不必要的 API 调用。

去抖动输入以防止对 API 的过度调用

我们还可以使用 useCallback hook 去抖动调用 API(例如搜索 API)的用户输入。

消除用户输入的抖动可防止对 API 的过度调用,从而防止服务器过载,或者如果您使用的是第 3 方 API,则可防止 API 成本。

让我们在代码示例中编写代码:

import React, { useState, useEffect, useCallback } from "react";
import debounce from "lodash.debounce";

function WikiSearch({ searchDelay = 300 }) {
  const [searchTerm, setSearchTerm] = useState("");
  const [results, setResults] = useState([]);

  const performSearch = async (term) => {
    const response = await fetch(
      `https://en.wikipedia.org/w/api.php?action=query&list=search&format=json&origin=*&srsearch=${term}`
    );
    const data = await response.json();
    setResults(data.query.search);
  };

  const debouncedSearch = useCallback(
    debounce((term) => {
      performSearch(term);
    }, searchDelay),
    [searchDelay]
  );

  useEffect(() => {
    if (searchTerm) {
      debouncedSearch(searchTerm);
    } else {
      setResults([]);
    }
  }, [searchTerm, debouncedSearch]);

  return (
    <div>
      <h3>Wiki Search Engine</h3>
      <hr />
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search Wikipedia."
      />
      {results.length > 0 ? (
        <h1>Wikipedia Search Results</h1>
      ) : (
        <div>
          <br />
          <strong>Nothing Found</strong>
        </div>
      )}
      <ul>
        {results.map((result) => (
          <li key={result.pageid}>
            <a
              href={`https://en.wikipedia.org/?curid=${result.pageid}`}
              target="_blank"
              rel="noreferrer"
            >
              {result.title}
            </a>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default WikiSearch;

在上面的代码示例中,我们使用维基百科 API 来搜索维基百科以获取页面列表。

我们正在添加 300 毫秒的默认去抖动并使用 lodash.debounce 库并使用创建缓存的去抖动方法 useCallback

我们创建了一个 WikiSearch 接受可选 searchDelay.

接下来,我们创建了一个名为 as 的方法 performSearch,该方法将获取信息并将其设置在结果日期变量中。

使用 useCallback,我们创建了一个 debouncedSearch 方法并调用了  搜索useCallback 挂钩 。debounce

最后在 useEffect 钩子中我们调用了 debouncedSearch方法。

这是演示:

处理列表中的事件

当您的项目列表中的每个项目都有一个事件处理程序时,我们可以使用 来 useCallback 缓存处理程序函数。

为了演示这一点,我们将创建一个 TodoList 组件,它将显示 Todo 列表。

import React, { useState, useCallback } from "react";

const TodoItem = React.memo(({ item, onToggle }) => (
  <li>
    <input
      type="checkbox"
      checked={item.completed}
      onChange={() => onToggle(item.id)}
    />
    {item.name}
  </li>
));

function TodoListComponent() {
  const [todos, setTodos] = useState([
    { id: 1, name: "Todo 1", completed: false },
    { id: 2, name: "Todo 2", completed: false },
    { id: 3, name: "Todo 3", completed: false },
    { id: 4, name: "Todo 4", completed: false }
  ]);

  const handleToggle = useCallback((id) => {
    setTodos((prevTodos) =>
      prevTodos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  }, []);

  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem key={todo.id} item={todo} onToggle={handleToggle} />
      ))}
    </ul>
  );
}

export default TodoListComponent;

我们已经创建了 handleToggle 方法并使用 缓存它 useCallback,并将 传递 handleToggle 给  列表。

结论

在这篇博文中,我们学习了如何使用 useCallback 以及真实场景和示例。

你可能感兴趣的:(javascript,react.js,开发语言)