ReactJS - Автосохранение с несколькими входами - лучшие практики - PullRequest
0 голосов
/ 03 августа 2020

Цель, которую я хочу достичь, - реализовать функцию автосохранения без ущерба для производительности (бесполезные рендеры и т. Д. c). В идеале, когда произойдет автосохранение, состояние также будет обновлено. Я создал пример компонента с 3 входами, в этом примере компонент перерисовывается при каждом нажатии клавиши. У меня также есть хук useEffect, в котором я ищу изменения данных, а затем сохраняю их после 1se c. ChildComponent используется для предварительного просмотра входных данных.

function App(props) {
  
  const timer = React.useRef(null);  
  const [data, setData] = React.useState(props.inputData);

  React.useEffect(() => {
    clearTimeout(timer.current)
    timer.current = setTimeout(() => {
      console.log("Saving call...", data)
    }, 1000)
  }, [data])

  const inputChangeHandler = (e, type) => {
    if (type === "first") {
      setData({ ...data, first: e.target.value })
    } else if (type === "second") {
      setData({ ...data, second: e.target.value })
    } else if (type === "third") {
      setData({ ...data, third: e.target.value })
    }
  }

  return (
    <>
      <div className="inputFields">
        <input 
          defaultValue={data.first} 
          type="text" 
          onChange={(e) => inputChangeHandler(e, "first")} 
        />
        <input 
          defaultValue={data.second} 
          type="text" 
          onChange={(e) => inputChangeHandler(e, "second")} 
        />
        <input 
          defaultValue={data.third} 
          type="text" 
          onChange={(e) => inputChangeHandler(e, "third")} 
        />
      </div>
      <ChildComponent data={data} />
    </>
  )
}

Я читал о debounce, но моя реализация не сработала. Кто-нибудь сталкивался с той же проблемой?

Ниже моя реализация debounce с использованием loda sh:

React.useEffect(() => {
  console.log("Saving call...", data)
}, [data])

const delayedSave = React.useCallback(_.debounce(value => setData(value), 1000), []);

const inputChangeHandler = (e, type) => {
  if (type === "first") {
    let obj = { ...data };
    obj.first = e.target.value;
    delayedSave(obj)
  } else if (type === "second") {
    let obj = { ...data };
    obj.second = e.target.value;
    delayedSave(obj)
  } else if (type === "third") {
    let obj = { ...data };
    obj.third = e.target.value;
    delayedSave(obj)
  }
}

Проблема в том, что если пользователь набирает сразу (до 1se c задержка) от первого ввода ко второму сохраняется только последний ввод пользователя.

Ответы [ 2 ]

1 голос
/ 06 августа 2020

Проблема в вашей реализации заключается в том, что таймер установлен в закрытии (в useEffect) с использованием data вашего компонента до запуска таймера. Вы должны запустить таймер после изменения data (или newData в моем предложении по реализации). Что-то вроде:

function App(props) {
  const [data, setData] = React.useState(props.inputData);
  const { current } = React.useRef({ data, timer: null });

  const inputChangeHandler = (e, type) => {
    current.data = { ...current.data, [type]: e.target.value };

    if(current.timer) clearTimeout(current.timer);

    current.timer = setTimeout(() => {
      current.timer = null;
      setData(current.data);
      console.log("Saving...", current.data);
    }, 1000);
  }

  return (
    <>
      <input defaultValue={data.first} type="text" onChange={(e) => inputChangeHandler(e, "first")} />
      <input defaultValue={data.second} type="text" onChange={(e) => inputChangeHandler(e, "second")} />
      <input defaultValue={data.third} type="text" onChange={(e) => inputChangeHandler(e, "third")} />
    </>
  );
}
0 голосов
/ 03 августа 2020

Вы должны установить для идентификатора таймера значение null внутри обработчика таймера.

React.useEffect(()=>{
    clearTimeout(timer.current)
    timer.current = setTimeout(()=>{
      timer.current = null;
      console.log("Saving...",data)
    },1000)
},[data])
...