使用 React 更改输入并折射其他几个输入上的更改

Change an input and reflact change on several other inputs with React

提问人:albertoivo 提问时间:10/25/2023 最后编辑:T.J. Crowderalbertoivo 更新时间:10/25/2023 访问量:46

问:

我有 19 个输入,当其中一个输入发生变化时,所有其他输入都会通过计算反映变化。

<input value={mph} onChange={(e) => cmph(e.target.value, setKph, setMps)}
<input value={kph} onChange={(e) => ckph(e.target.value, setMph, setMps)}
<input value={mps} onChange={(e) => cmps(e.target.value, setKph, setMph)}

对于每个输入,我都有一个状态。所以我使用了 19 个变量。并且所有输入都有一个 .useStateonChange

  const [mph, setMph] = useState() // miles per hour
  const [kph, setKph] = useState() // kiliometers per hour
  const [mps, setMps] = useState() // miles per seconds

因此,当任何输入发生更改时,它会调用其唯一函数进行计算并设置状态 18 次(对于其他每个输入)。onChange

每个输入都调用自己的函数来计算其他输入的值。它简单的数学。没有数据库或终端节点调用。

function cmph(mph, setKph, setMps) {
  const kph = mph * 1.60934
  setKph(Math.round((kph + Number.EPSILON) * 100) / 100)

  const mps = mph / 2.237
  setMps(Math.round((mph + Number.EPSILON) * 100) / 100)
}

function ckph(kph, setMph, setMps) {
  const mph = kph / 1.60934
  setMph(Math.round((mph + Number.EPSILON) * 100) / 100)

  const mps = mph / 2.237
  setMps(Math.round((mps + Number.EPSILON) * 100) / 100)
 }

function cmps(mps, setKph, setMph) {
  const mph = mps * 2.237
  setMph(mph)

  const kph = mph * 1.60934
  setKph(kph)
}

我想要的只是更改任何输入,所有其他输入都会自动反映新值。

PS:我减少了三个输入,所以例子会更小。但我有 19 个。

我该如何解决这个问题?

堆栈片段:

const { useState } = React;

const Example = () => {
    const [mph, setMph] = useState(); // miles per hour
    const [kph, setKph] = useState(); // kiliometers per hour
    const [mps, setMps] = useState(); // miles per seconds

    function cmph(mph, setKph, setMps) {
        const kph = mph * 1.60934;
        setKph(Math.round((kph + Number.EPSILON) * 100) / 100);

        const mps = mph / 2.237;
        setMps(Math.round((mph + Number.EPSILON) * 100) / 100);
    }

    function ckph(kph, setMph, setMps) {
        const mph = kph / 1.60934;
        setMph(Math.round((mph + Number.EPSILON) * 100) / 100);

        const mps = mph / 2.237;
        setMps(Math.round((mps + Number.EPSILON) * 100) / 100);
    }

    function cmps(mps, setKph, setMph) {
        const mph = mps * 2.237;
        setMph(mph);

        const kph = mph * 1.60934;
        setKph(kph);
    }

    return (
        <div>
            <input
                value={mph}
                size="5"
                onChange={(e) => cmph(e.target.value, setKph, setMps)}
            />
            <input
                value={kph}
                size="5"
                onChange={(e) => ckph(e.target.value, setMph, setMps)}
            />
            <input
                value={mps}
                size="5"
                onChange={(e) => cmps(e.target.value, setKph, setMph)}
            />
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

javascript reactjs input react-hooks onchange

评论

0赞 Violet Rosenzweig 10/25/2023
您可以使用一个状态变量,并从这个单一事实来源派生输入值。无论如何,了解您试图为其他输入“计算”什么会有所帮助;它是否简单,例如第一个输入显示 X,第二个输入显示 X*2?或者,它是否更复杂,涉及数据库调用?你到底想做什么?
0赞 albertoivo 10/25/2023
@T.J.Crowder:我现在编辑了。我希望它更好。
0赞 albertoivo 10/25/2023
@T.J.Crowder:我不明白。我只省略了执行简单数学运算并设置状态的函数。我不认为这是问题所在。但没关系。这就是所有代码。我真的希望它现在更好,因为我不知道我还能做些什么来改善这个问题。

答:

1赞 0stone0 10/25/2023 #1

有大量的 React-Ways 可以解决您的问题。

其中之一是使用带有每个输入/数据类型键的 Object。

然后,在输入中,使用属性查看哪个值发生了变化,将该值设置为新状态。对于状态对象中的所有其他键,找到正确的键并应用它。onChangedata-speedconvertFunction

我已经用一个简单的 / 函数替换了所有 / 函数,以保持演示的干净。cmpsckphconvertFunction

const { useState } = React;

const Example = () => {
    const [data, setData] = useState({ 
      mph: 0,
      kph: 0,
      mps: 0
    });
    
    const convertFunction = (n) => Math.round(n * Math.random() * 16);
    
    const onInput = (e) => {
        const changedInputValue = +e.target.value;
        const changedInputType = e.target.getAttribute('speed');
        
        setData(p => {
            const newData = { ...data };
            for (var speed in p) {
                if (speed === changedInputType) {
                    newData[speed] = changedInputValue;
                } else {
                    // Choose correct convert function
                    newData[speed] = convertFunction(changedInputValue)
                }
            }
            return newData;
        });
    }

    return (
        <div>
            <input
                value={data.mph}
                size="5"
                speed='mph'
                onChange={onInput}
            />
            <input
                value={data.kph}
                size="5"
                speed='kph'
                onChange={onInput}
            />
            <input
                value={data.mps}
                size="5"
                speed='mps'
                onChange={onInput}
            />
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

评论

1赞 albertoivo 10/25/2023
我真的很喜欢你的解决方案。但我会继续使用 T.J Crowder 解决方案,因为它更简单,而且我不必对原始代码进行太多更改。但是非常感谢!
1赞 0stone0 10/25/2023
没关系,希望我的回答对;)有所帮助如果您添加更多输入,这似乎更具可扩展性!
0赞 albertoivo 10/25/2023
非常正确!@0stone0
1赞 T.J. Crowder 10/25/2023 #2

问题在于您的输入是受控的,这意味着它们的值不会自动更新,除非您设置了用于设置其 .它最初起作用的原因是,如果不给 提供初始值,你就默认为 ,并使 React 认为输入不受控制。一旦更改值,输入就会从不受控制变为受控,并且停止工作。(如果你使用 React 库的开发版本进行开发,你会收到一个很大的警告。有关受控输入与非受控输入的更多信息,请参阅文档valueuseStateundefinedvalue={undefined}

相反:

  • 使用初始值,以便它们不会默认为useStateundefined
  • 处理每个输入的事件时,更新控制它的状态成员change

例如,对于:mph

const mphOnChange = ({ currentTarget: { value } }) => {
    setMph(value); // <======================= This is what was missing
    cmph(value, setKph, setMps);
};

工作示例(这些默认值可能需要调整):

const { useState } = React;

function cmph(mph, setKph, setMps) {
    const kph = mph * 1.60934;
    setKph(Math.round((kph + Number.EPSILON) * 100) / 100);

    const mps = mph / 2.237;
    setMps(Math.round((mph + Number.EPSILON) * 100) / 100);
}

function ckph(kph, setMph, setMps) {
    const mph = kph / 1.60934;
    setMph(Math.round((mph + Number.EPSILON) * 100) / 100);

    const mps = mph / 2.237;
    setMps(Math.round((mps + Number.EPSILON) * 100) / 100);
}

function cmps(mps, setKph, setMph) {
    const mph = mps * 2.237;
    setMph(mph);

    const kph = mph * 1.60934;
    setKph(kph);
}

const Example = () => {
    const [mph, setMph] = useState(0); // miles per hour
    const [kph, setKph] = useState(0); // kiliometers per hour
    const [mps, setMps] = useState(0); // miles per seconds

    const mphOnChange = ({ currentTarget: { value } }) => {
        setMph(value);
        // Recommend explicit conversion to number here
        cmph(value, setKph, setMps);
    };

    const kphOnChange = ({ currentTarget: { value } }) => {
        setKph(value);
        // Recommend explicit conversion to number here
        ckph(value, setMph, setMps);
    };

    const mpsOnChange = ({ currentTarget: { value } }) => {
        setMps(value);
        // Recommend explicit conversion to number here
        cmps(value, setKph, setMph);
    };

    return (
        <div>
            <input value={mph} size="5" onChange={mphOnChange} />
            <input value={kph} size="5" onChange={kphOnChange} />
            <input value={mps} size="5" onChange={mpsOnChange} />
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>