【问题标题】:input value not updating when mutating state改变状态时输入值不更新
【发布时间】:2021-09-27 07:21:18
【问题描述】:

在为学习目的创建一个小项目时,我遇到了更新输入值的问题。这是组件(我已尝试将其减少到最低限度)。

function TipSelector({selections, onTipChanged}: {selections: TipSelectorItem[], onTipChanged?:(tipPercent:number)=>void}) {
    
    const [controls, setControls] = useState<any>([]);
    const [tip, setTip] = useState<string>("0");

    function customTipChanged(percent: string)  {
        setTip(percent);
    }

    //Build controls
    function buildControls()
    {
        let controlList: any[] = [];
        controlList.push(<input className={styles.input} value={tip.toString()} onChange={(event)=> {customTipChanged(event.target.value)}}></input>);
        setControls(controlList);
    }
    
    useEffect(()=>{
        console.log("TipSelector: useEffect");
        buildControls();

        return ()=> {
            console.log("unmounts");
        }
    },[])
    
    console.log("TipSelector: Render -> "+tip);
    return (
        <div className={styles.tipSelector}>
            <span className={globalStyles.label}>Select Tip %</span>
            <div className={styles.btnContainer}>
                {
                   controls
                }
            </div>
        </div>
    );
}

如果我将输入的创建直接移动到 return() 语句中,则值会正确更新。

【问题讨论】:

  • 在状态中存储组件是一种反模式。而是在 state 中保留有关组件的数据列表,并在 return 每个渲染中创建组件。
  • @BrianThompson 我理解你所说的状态反模式中的组件是什么意思,但我不明白的是你的第二个陈述。我不是直接改变状态,而是通过 setControls 钩子。我的意思是我没有做controls.push(X)。我错过了什么吗? (你刚刚删除了我提到的评论吗?)
  • 我确实删除了该评论,我读错了您的代码并且不正确。
  • 好的,无论如何感谢其他反模式。

标签: reactjs typescript state


【解决方案1】:

我会将您的输入移出该组件,并让它们在 TipSelector 之外管理自己的状态。

见:

https://codesandbox.io/s/naughty-http-d38w9

例如:

import { useState, useEffect } from "react";
import CustomInput from "./Input";

function TipSelector({ selections, onTipChanged }) {
  const [controls, setControls] = useState([]);

  //Build controls
  function buildControls() {
    let controlList = [];
    controlList.push(<CustomInput />);
    controlList.push(<CustomInput />);
    setControls(controlList);
  }

  useEffect(() => {
    buildControls();

    return () => {
      console.log("unmounts");
    };
  }, []);

  return (
    <div>
      <span>Select Tip %</span>
      <div>{controls}</div>
    </div>
  );
}

export default TipSelector;

import { useState, useEffect } from "react";

function CustomInput() {
  const [tip, setTip] = useState("0");

  function customTipChanged(percent) {
    setTip(percent);
  }

  return (
    <input
      value={tip.toString()}
      onChange={(event) => {
        customTipChanged(event.target.value);
      }}
    ></input>
  );
}

export default CustomInput;

【讨论】:

  • 感谢您的努力,但这是更大组件的一部分,我不会将输入状态移动到输入组件中。无论如何感谢您的建议。
【解决方案2】:

您只调用一次buildControls,而&lt;input ... 仅在该一次获得其值。

每当 React 重新渲染您的组件时(例如,因为某些状态更改),您的 {controls} 将告诉 React 使用旧值渲染原始 &lt;input ...

我不确定您为什么将控件存储在状态变量中?没有必要这样做,而且正如您所注意到的,它使事情复杂化了很多。您基本上还需要一个 renderControls() 函数,您可以将 {controls} 替换为。

【讨论】:

  • 它存储在一个数组中,因为某些组件在单击时会发生变异。例如,有一个“自定义”按钮,当单击该按钮时,该按钮将更改为用户输入自定义数量的输入。关于这个问题,我太专注了,以至于我没有注意到 buildControls 方法只在第一次渲染时被调用。谢谢你。
  • 您可以在代码中使用“条件渲染”。例如。 {controls} 可以是{showCustom &amp;&amp; &lt;Custom/&gt;} 甚至是{showCustom ? &lt;Custom/&gt; : &lt;button&gt;Show custom&lt;/button&gt;}
  • 是的,我知道。我想我将不得不重新考虑这个组件。这并不像一开始看起来那么容易:)
猜你喜欢
  • 2020-01-28
  • 2021-11-27
  • 2018-12-18
  • 2019-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-22
相关资源
最近更新 更多