如何去抖动受控输入?

Phi*_*ume 5 javascript lodash reactjs

我目前正在努力应对反应输入并从 lodash 中去抖动。大多数情况下,当我有一个表单时,我也有一个编辑选项,所以我需要一个受控组件来填充输入,value={state["targetValue"]}以便我可以填充和编辑该字段。

但是,如果组件受到控制,则 debounce 不起作用。我在 CodeSandbox 上做了一个简单的例子:https ://codesandbox.io/embed/icy-cloud-ydzj2 ? fontsize =14& hidenavigation =1& theme = dark

代码:

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { debounce } from "lodash";

import "./styles.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "",
      title: "",
      editMode: false
    };
    this.debouncedEvent = React.createRef();
  }

  debounceEvent(_fn, timer = 500, options = null) {
    this.debouncedEvent.current = debounce(_fn, timer, options);
    return e => {
      e.persist();
      return this.debouncedEvent.current(e);
    };
  }

  componentWillUnmount() {
    this.debouncedEvent.current.cancel();
  }

  onChangeValue = event => {
    const { name, value } = event.target;

    this.setState(() => {
      return { [name]: value };
    });
  };

  onRequestEdit = () => {
    this.setState({ name: "Abla blabla bla", editMode: true });
  };

  onCancelEdit = () => {
    if (this.state.editMode) this.setState({ name: "", editMode: false });
  };

  onSubmit = event => {
    event.preventDefault();
    console.log("Submiting", this.state.name);
  };

  render() {
    const { name, editMode } = this.state;
    const isSubmitOrEditLabel = editMode ? `Edit` : "Submit";
    console.log("rendering", name);
    return (
      <div className="App">
        <h1> How to debounce controlled input ?</h1>
        <button type="button" onClick={this.onRequestEdit}>
          Fill with dummy data
        </button>
        <button type="button" onClick={this.onCancelEdit}>
          Cancel Edit Mode
        </button>
        <div style={{ marginTop: "25px" }}>
          <label>
            Controlled / Can be used for editing but not with debounce
          </label>
          <form onSubmit={this.onSubmit}>
            <input
              required
              type="text"
              name="name"
              value={name}
              placeholder="type something"
              // onChange={this.onChangeValue}
              onChange={this.debounceEvent(this.onChangeValue)}
            />
            <button type="submit">{isSubmitOrEditLabel}</button>
          </form>
        </div>
        <div style={{ marginTop: "25px" }}>
          <label> Uncontrolled / Can't be used for editing </label>
          <form onSubmit={this.onSubmit}>
            <input
              required
              type="text"
              name="name"
              placeholder="type something"
              onChange={this.debounceEvent(this.onChangeValue)}
            />
            <button type="submit">{isSubmitOrEditLabel}</button>
          </form>
        </div>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Run Code Online (Sandbox Code Playgroud)

Ale*_*tov 6

乍一看这似乎不可能,但有一个简单的方法可以做到。只需在 React 组件外部创建 debounce 函数表达式即可。

这是一个伪示例,适用于带有 Hooks 的现代 React:

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


const getSearchResults = debounce((value, dispatch) => {
  dispatch(getDataFromAPI(value));
}, 800);



const SearchData = () => {
  const [inputValue, setInputValue] = useState("");

  ...

  useEffect(() => {
    getSearchResults(inputValue, dispatch);
  }, [inputValue]);

 ...

 return (
   <>
      <input
        type="text"
        placeholder="Search..."
        value={inputValue}
        onChange={e => setInputValue(e.target.value)}
      />
      ...
   </>
 );
};

export default SearchData;
Run Code Online (Sandbox Code Playgroud)

更新:及时我使用“useCallback”想出了一个更好的解决方案

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


const SearchData = () => {
  const [inputValue, setInputValue] = useState("");

  ...

  const getSearchResults = useCallback(
    debounce(value => {
      dispatch(getDataFromAPI(value));
    }, 800),
    []
  );

  useEffect(() => {
    getSearchResults(inputValue);
  }, [inputValue]);

 ...

 return (
   <>
      <input
        type="text"
        placeholder="Search..."
        value={inputValue}
        onChange={e => setInputValue(e.target.value)}
      />
      ...
   </>
 );
};
Run Code Online (Sandbox Code Playgroud)


Phi*_*ume 2

所以……显然,没有解决办法。输入从状态获取值。而去抖会阻止状态触发。

我使用 ReactDOM 做了一个解决方法。

import ReactDOM from "react-dom";

export const setFormDefaultValue = (obj, ref) => {
  if (ref && !ref.current) return;

  if (!obj || !obj instanceof Object) return;

  const _this = [
    ...ReactDOM.findDOMNode(ref.current).getElementsByClassName("form-control")
  ];

  if (_this.length > 0) {
    _this.forEach(el => {
      if (el.name in obj) el.value = obj[el.name];
      else console.error(`Object value for ${el.name} is missing...`);
    });
  }
};
Run Code Online (Sandbox Code Playgroud)

然后使用:

this.refForm = React.createRef();
setFormDefaultValue(this.state, refForm)
Run Code Online (Sandbox Code Playgroud)

这样我就可以用状态默认值填写表单并继续使用去抖。