【问题标题】:Can I avoid useEffect when siblings in a controlled component need to be kept in sync?当受控组件中的兄弟需要保持同步时,我可以避免使用 useEffect 吗?
【发布时间】:2023-01-12 08:51:54
【问题描述】:

通常我会有一个由两个输入组成的输入。例如,带有数字输入的滑块,或带有十六进制输入的颜色选择器。这些组件需要在用户完成对它们的操作时宣布状态更改,但它们需要相互通知每一个改变。每个都需要以比父级更细的粒度来跟踪另一个的变化。

例如,如果用户拖动滑块,则数字输入应始终代表滑块的值。当用户输入数字时,滑块应该跳来跳去并保持同步。当用户释放滑块时,应从组件触发 onChange 回调,以便父级可以更新状态。

为清楚起见:在下面的示例中,如果用户在左侧输入上单击“向上”10 次,我希望看到每个更改都反映在左侧输入上,但当组件失去焦点时,父项中只有 1 个更改。

下面是一些实现此行为的代码。这段代码确实确切地我想让它做什么。对于我的用例,该行为 100% 正确。但是,我不想在这个组件中有一个useEffect。那是我的问题“我怎样才能删除这个 useEffect?”

import "./styles.css";
import { useEffect, useState } from "react";

export default function App() {
  const [state, setState] = useState(0);

  // this is called whenever the user is "done" manipulating the compound input
  const handleChange = (change) => {
    console.log("change", change);
    setState(change);
  };

  return (
    <div className="App">
      <CompoundInput value={state} onChange={handleChange} />
    </div>
  );
}

function CompoundInput(props) {
  const [internalState, setInternalState] = useState(props.value);

  // this is a _controlled_ component, so this internal state
  // must also track the parent state
  useEffect(() => {
    setInternalState(props.value);
  }, [props.value]);

  // each input updates to reflect the state of the other input
  // but does so without the parent knowing
  return (
    <>
      <input
        type="number"
        value={internalState}
        onChange={(e) => setInternalState(e.target.value)}
        onBlur={(e) => props.onChange(e.target.value)}
      />
      <input
        type="number"
        value={internalState}
        onChange={(e) => setInternalState(e.target.value)}
        onBlur={(e) => props.onChange(e.target.value)}
      />
    </>
  );
}

https://codesandbox.io/s/compassionate-sun-8zc7k9?file=/src/App.js

由于 useState,我发现这个实现令人沮丧。我的感觉是,当遵循这种模式时,最终每个组件都需要一点 useEffect 来跟踪受控组件的内部状态。我的感觉是应该用useEffect来实现效果这并不是真正的效果。

有没有办法实现这个组件没有使用效果?

【问题讨论】:

  • 将 setState 向下传递给子组件,并在 onChange 触发时调用它们。然后状态将触发重新呈现您的组件,因此您不必使用 useEffect。 ,这对你的情况满意吗?
  • 不,也许我的问题不清楚:/父母应该只要在调用onBlur 时更新。 onChange 回调用于在用户更新它们时保持左右输入同步。如果用户在左侧输入中键入 123,然后在外部单击,则右侧输入将取值 1,然后是 12,然后是 123...但是 onChange 回调将仅通过 123 调用一次。那有意义吗?
  • 我认为根本不需要效果挂钩。如果你改变父state,孩子应该重新渲染,因为它的道具改变了~codesandbox.io/s/dreamy-ellis-1086v1?file=/src/App.js
  • @Ziggy 不正是我添加的链接中发生的事情吗?请注意,当您使用箭头按钮时,数字输入不会触发模糊事件
  • 直接回答你的问题......是的。把效果钩去掉就好了,多余。我不确定这是否值得一个完整的答案

标签: javascript reactjs react-hooks


【解决方案1】:

有没有办法在没有 useEffect 的情况下实现这个组件?

绝对,只是省略它。

因为您的组件具有 state 值作为属性,所以如果父级更改该状态,它将重新呈现。

在内部,它将保持自己的状态,并且仅在蓝色的事件触发 onChange 道具功能。

这个例子更清楚地展示了它

import { useState } from "react";

export default function App() {
  const [state, setState] = useState(0);

  const handleChange = (change) => {
    console.log("change", change);
    setState(change);
  };

  return (
    <div className="App">
      <fieldset>
        <legend>App state</legend>
        <input type="number" onChange={(e) => handleChange(e.target.value)} value={state}/>
        <pre>state = {state}</pre>
      </fieldset>
      <fieldset>
        <legend>CompoundInput</legend>
        <CompoundInput value={state} onChange={handleChange} key={state} />
      </fieldset>
    </div>
  );
}

function CompoundInput({ value, onChange }) {
  const [internalState, setInternalState] = useState(value);

  return (
    <>
      <input
        type="number"
        value={internalState}
        onChange={(e) => setInternalState(e.target.value)}
        onBlur={(e) => onChange(e.target.value)}
      />
      <pre>internalState = {internalState}</pre>
    </>
  );
}

【讨论】:

  • 你能扩展这个答案吗?
  • 您是否建议我可以删除 internalStateuseEffect 而不是在父级中设置 key={state}? (注意:state.value 将始终未定义,因为整数没有属性 value)。
猜你喜欢
  • 2018-04-05
  • 2019-05-17
  • 1970-01-01
  • 2021-08-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 2018-05-26
相关资源
最近更新 更多