如何用React Hooks进行脱钩

66 阅读3分钟

在这篇文章中,我将使用React Hooks API来实现debouncing。如果你对debouncing不熟悉,它只是一种机制,用于推迟一个函数的执行。通常情况下,这将在javascript中实现,以防止一个潜在的昂贵的操作(例如,从后端请求数据)被过早执行。现实生活中的一个例子是一个typeahead列表,它只在用户停止输入后的一段时间内执行搜索。

设置

我将使用一个例子,我们根据用户的输入来过滤美国各州的JSON文件。我们不希望在用户停止输入之前过滤列表,所以我们要实现跳转。我们的JSON文件将采用以下格式。

[
  {
    "name": "Alabama",
    "abbreviation": "AL"
  },
  {
    "name": "Alaska",
    "abbreviation": "AK"
  },
  ...
]

为了简单起见,我们将只根据用户的输入来过滤name

实现无跳转的过滤钩子

让我们快速实现没有去抖的功能。这意味着在用户输入时,状态列表将被自动过滤。

import React, { useState, useEffect, Fragment } from 'react';
import states from './states';

const App = () => {
  const [search, setSearch] = useState('');
  const [filteredStates, setFilteredStates] = useState(states);

  useEffect(() => {
    const filter = states.filter((state) => {
      return state.name.toLowerCase().includes(search.toLowerCase());
    });

    setFilteredStates(filter);
  }, [search]);

  return (
    <Fragment>
      <input value={search} onChange={(e) => setSearch(e.target.value)} />
      <ul>
        {filteredStates &&
          filteredStates.map((state) => {
            return <li key={state.abbreviation}>{state.name}</li>;
          })}
      </ul>
    </Fragment>
  );
};

export default App;

当我们的输入元素发生变化时,setSearch 被调用,这将改变我们的search 值,重新渲染组件。useEffect 钩子就像我们所指出的那样,只要search 发生变化,它就会运行。在useEffect 钩子中,我们根据search 的值来过滤states

如果我们在这时运行我们的应用程序,我们会看到它的功能符合要求。

添加去抖动

为了增加调试,我们可以直觉地认为,useEffect 钩子应该用一个定时器来更新。让我们添加一个有1秒等待时间的setTimeout ,看看效果如何。

useEffect(() => {
  setTimeout(() => {
    const filter = states.filter((state) => {
      return state.name.toLowerCase().includes(search.toLowerCase());
    });

    setFilteredStates(filter);
  }, 1000);
}, [search]);

这并不完全正确。它确实延迟了过滤,但它并没有等到你停止输入。基本上,应用程序现在只是等待一秒钟,然后开始追赶你正在输入的内容。这不是理想的行为--完全相同的过滤量正在发生。

有一个简单的解决方法:useEffect 钩子提供了一个 "清理 "机制,当组件下一次重新显示时将会运行。我们所要做的就是从传递给useEffect 钩子的第一个函数中返回清理函数。在我们的例子中,我们只需要取消setTimeout 。如果在用户输入更多信息之前,1秒还没有过去,setTimeout 将被取消,过滤器将不会被应用。

useEffect(() => {
  const timer = setTimeout(() => {
    const filter = states.filter((state) => {
      return state.name.toLowerCase().includes(search.toLowerCase());
    });

    setFilteredStates(filter);
  }, 1000);

  return () => clearTimeout(timer);
}, [search]);

成功了!我们现在要等到用户最后一次输入后的1秒来过滤这些状态。完整的代码可以在下面查看。

App.js

import React, { useState, useEffect, Fragment } from 'react';
import states from './states';

const App = () => {
  console.log('render');
  const [search, setSearch] = useState('');
  const [filteredStates, setFilteredStates] = useState(states);

  useEffect(() => {
    const timer = setTimeout(() => {
      const filter = states.filter((state) => {
        return state.name.toLowerCase().includes(search.toLowerCase());
      });

      setFilteredStates(filter);
    }, 1000);

    return () => clearTimeout(timer);
  }, [search]);

  return (
    <Fragment>
      <input value={search} onChange={(e) => setSearch(e.target.value)} />
      <ul>
        {filteredStates &&
          filteredStates.map((state) => {
            return <li key={state.abbreviation}>{state.name}</li>;
          })}
      </ul>
    </Fragment>
  );
};

export default App;

states.json

[
  {
    "name": "Alabama",
    "abbreviation": "AL"
  },
  {
    "name": "Alaska",
    "abbreviation": "AK"
  },
  {
    "name": "American Samoa",
    "abbreviation": "AS"
  },
  {
    "name": "Arizona",
    "abbreviation": "AZ"
  },
  {
    "name": "Arkansas",
    "abbreviation": "AR"
  },
  {
    "name": "California",
    "abbreviation": "CA"
  },
  {
    "name": "Colorado",
    "abbreviation": "CO"
  },
  {
    "name": "Connecticut",
    "abbreviation": "CT"
  },
  {
    "name": "Delaware",
    "abbreviation": "DE"
  },
  {
    "name": "District Of Columbia",
    "abbreviation": "DC"
  },
  {
    "name": "Federated States Of Micronesia",
    "abbreviation": "FM"
  },
  {
    "name": "Florida",
    "abbreviation": "FL"
  },
  {
    "name": "Georgia",
    "abbreviation": "GA"
  },
  {
    "name": "Guam",
    "abbreviation": "GU"
  },
  {
    "name": "Hawaii",
    "abbreviation": "HI"
  },
  {
    "name": "Idaho",
    "abbreviation": "ID"
  },
  {
    "name": "Illinois",
    "abbreviation": "IL"
  },
  {
    "name": "Indiana",
    "abbreviation": "IN"
  },
  {
    "name": "Iowa",
    "abbreviation": "IA"
  },
  {
    "name": "Kansas",
    "abbreviation": "KS"
  },
  {
    "name": "Kentucky",
    "abbreviation": "KY"
  },
  {
    "name": "Louisiana",
    "abbreviation": "LA"
  },
  {
    "name": "Maine",
    "abbreviation": "ME"
  },
  {
    "name": "Marshall Islands",
    "abbreviation": "MH"
  },
  {
    "name": "Maryland",
    "abbreviation": "MD"
  },
  {
    "name": "Massachusetts",
    "abbreviation": "MA"
  },
  {
    "name": "Michigan",
    "abbreviation": "MI"
  },
  {
    "name": "Minnesota",
    "abbreviation": "MN"
  },
  {
    "name": "Mississippi",
    "abbreviation": "MS"
  },
  {
    "name": "Missouri",
    "abbreviation": "MO"
  },
  {
    "name": "Montana",
    "abbreviation": "MT"
  },
  {
    "name": "Nebraska",
    "abbreviation": "NE"
  },
  {
    "name": "Nevada",
    "abbreviation": "NV"
  },
  {
    "name": "New Hampshire",
    "abbreviation": "NH"
  },
  {
    "name": "New Jersey",
    "abbreviation": "NJ"
  },
  {
    "name": "New Mexico",
    "abbreviation": "NM"
  },
  {
    "name": "New York",
    "abbreviation": "NY"
  },
  {
    "name": "North Carolina",
    "abbreviation": "NC"
  },
  {
    "name": "North Dakota",
    "abbreviation": "ND"
  },
  {
    "name": "Northern Mariana Islands",
    "abbreviation": "MP"
  },
  {
    "name": "Ohio",
    "abbreviation": "OH"
  },
  {
    "name": "Oklahoma",
    "abbreviation": "OK"
  },
  {
    "name": "Oregon",
    "abbreviation": "OR"
  },
  {
    "name": "Palau",
    "abbreviation": "PW"
  },
  {
    "name": "Pennsylvania",
    "abbreviation": "PA"
  },
  {
    "name": "Puerto Rico",
    "abbreviation": "PR"
  },
  {
    "name": "Rhode Island",
    "abbreviation": "RI"
  },
  {
    "name": "South Carolina",
    "abbreviation": "SC"
  },
  {
    "name": "South Dakota",
    "abbreviation": "SD"
  },
  {
    "name": "Tennessee",
    "abbreviation": "TN"
  },
  {
    "name": "Texas",
    "abbreviation": "TX"
  },
  {
    "name": "Utah",
    "abbreviation": "UT"
  },
  {
    "name": "Vermont",
    "abbreviation": "VT"
  },
  {
    "name": "Virgin Islands",
    "abbreviation": "VI"
  },
  {
    "name": "Virginia",
    "abbreviation": "VA"
  },
  {
    "name": "Washington",
    "abbreviation": "WA"
  },
  {
    "name": "West Virginia",
    "abbreviation": "WV"
  },
  {
    "name": "Wisconsin",
    "abbreviation": "WI"
  },
  {
    "name": "Wyoming",
    "abbreviation": "WY"
  }
]