如何使用钩子消除功能组件中的回调

时间:2019-05-23 23:02:25

标签: reactjs react-hooks

我如何在React Functional Component去抖动的回调中获取实际的prop值,它在React Class Component中起作用,但是我不知道如何使用钩子在功能组件中实现此行为。

import React from "react";
import ReactDOM from "react-dom";
import debounce from "lodash.debounce";

const TestFunc = ({ count, onClick }) => {
  const handleClick = debounce(() => {
    onClick();
    console.log(count);
  }, 500);

  return (
    <div>
      <button type="button" onClick={handleClick}>
        Func: {count}
      </button>
    </div>
  );
};

class TestClass extends React.Component {
  handleClick = debounce(() => {
    this.props.onClick();
    console.log(this.props.count);
  }, 500);

  render() {
    return (
      <div>
        <button type="button" onClick={this.handleClick}>
          Class: {this.props.count}
        </button>
      </div>
    );
  }
}

const App = () => {
  const [countClass, setCountClass] = React.useState(0);
  const [countFunc, setCountFunc] = React.useState(0);

  return (
    <div>
      <TestFunc count={countFunc} onClick={() => setCountFunc(countFunc + 1)} />
      <TestClass
        count={countClass}
        onClick={() => setCountClass(countClass + 1)}
      />
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));

当您单击功能组件按钮时,它将先前的count属性值记录到控制台,但是通过调用onClick处理程序已将其更改,同时类组件按钮将记录实际的由count处理程序递增后的onClick道具值。那么,如何获取功能组件中的实际prop值?

3 个答案:

答案 0 :(得分:3)

这是一个简单的防抖钩(用TypeScript编写)

import { useEffect, useRef } from "react";

export function useDebouncedCallback<A extends any[]>(
  callback: (...args: A) => void,
  wait: number
) {
  // track args & timeout handle between calls
  const argsRef = useRef<A>();
  const timeout = useRef<ReturnType<typeof setTimeout>>();

  function cleanup() {
    if(timeout.current) {
      clearTimeout(timeout.current);
    }
  }

  // make sure our timeout gets cleared if
  // our consuming component gets unmounted
  useEffect(() => cleanup, []);

  return function debouncedCallback(
    ...args: A
  ) {
    // capture latest args
    argsRef.current = args;

    // clear debounce timer
    cleanup();

    // start waiting again
    timeout.current = setTimeout(() => {
      if(argsRef.current) {
        callback(...argsRef.current);
      }
    }, wait);
  };
}

您的用例示例:

const handleClick = useDebouncedCallback(() => {
  onClick();
  console.log(count);
}, 500);

... 

<button type="button" onClick={handleClick}>
  Func: {count}
</button>

也适用于传递参数的情况:

const handleChange = useDebouncedCallback((event) => {
  console.log(event.currentTarget.value);
}, 500);

<input onChange={handleChange}/>

答案 1 :(得分:1)

您需要进行一些更改才能将debounced method与钩子一起使用

  1. 您需要使用useCallback钩子,以便在初始渲染时仅创建一次去抖动功能。
  2. 现在,如果您必须确保去抖动的值在执行时获得正确的计数值,则需要将其作为参数传递,否则它将在创建时使用其封闭中的值作为初始计数值。
  3. 您需要使用父级中的回调模式(例如setCountFunc(count => count + 1))来更新onClick方法调用上的计数值,以便子组件使用更新后的值重新呈现

下面的工作演示

const TestFunc = ({ count, onClick }) => {
  const handleClick = React.useCallback((count) =>{
     const click = _.debounce((count) => {
          onClick();
          console.log(count);
     }, 500)
     click(count);
 }, []);

  console.log(count, 'render');
  return (
    <div>
      <button type="button" onClick={() => handleClick(count)}>
        Func: {count}
      </button>
    </div>
  );
};

class TestClass extends React.Component {
  handleClick = _.debounce(() => {
    this.props.onClick();
    console.log(this.props.count);
  }, 500);

  render() {
    return (
      <div>
        <button type="button" onClick={this.handleClick}>
          Class: {this.props.count}
        </button>
      </div>
    );
  }
}

const App = () => {
  const [countClass, setCountClass] = React.useState(0);
  const [countFunc, setCountFunc] = React.useState(0);

  return (
    <div>
      <TestFunc count={countFunc} onClick={() => setCountFunc(count => count + 1)} />
      <TestClass
        count={countClass}
        onClick={() => setCountClass(countClass + 1)}
      />
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root" />

答案 2 :(得分:1)

用于功能组件中的去抖动API调用

<input
 type="text"
 placeholder="Search"
 onChange={(e) => search(e.target.value)}
/>
=========================================================================
  const [typingTimeout, setTypingTimeout] = useState(0);
const search = async (value) => {
    if (typingTimeout) {
      clearTimeout(typingTimeout);
    }
    setTypingTimeout( setTimeout(() => {
        goToSearch(value);
      }, 1000)
    );

  }
  const goToSearch = async (value) => {
    const response = await getData(args);
  }

用于类组件 与上述相同,但搜索功能有所改变

const search = (event.target.value) =>{
    if (this.state.typingTimeout) {
      clearTimeout(this.state.typingTimeout);
    }
    this.setState({
      typingTimeout: setTimeout(()=> {
        this.goToSearch(event.target.value);
      }, 1000)
    });
}