简体   繁体   English

(反应)如何根据动态键入的其他字段的计算结果更新输入字段

[英](React) How to update input field based on dynamically typed other fields’ calculation results

So I have this problem, banging my head to the wall for the last several days, where I have a simple form, there might be more than one divs with four inputs but they are all related to each other. 因此,我遇到了这个问题,在过去的几天里,我一直很努力地工作,这里有一个简单的表格,可能有多个输入多个div的div,但它们彼此相关。 What I'm trying to do is to calculate the last input's in a div value which is based on the rest of the inputs' value and are typed dynamically and then to setState() to return a new state. 我想做的是计算div值中的最后一个输入,该值基于其余输入的值并被动态键入,然后输入setState()以返回新状态。 I have handler functions (to add, remove or change value of input fields) which work when I don't need to handle this particular update. 我有处理程序功能(用于添加,删除或更改输入字段的值),该功能在我不需要处理此特定更新时可以工作。 Now here's the code: 现在这里是代码:

class App extends React.Component {

  state = {
      bank: 10,
      totalPrice: 0,
      hands: [
          {
              name: "hand1",
              backPrice: 0,
              bankPrice: 0,
              commision: 0,
              realPrice: 0
          }
      ],
      handCount: 1
  };

  componentDidUpdate(prevProps, prevState) {
    const hands = this.state.hands;

    // initialized variables needed to calculate a result
    const totalPrice = hands.reduce((total, hand) => {
        return (hand.backPrice || 1) * total;
        }, 1);

    // lastHandPrice is ALWAYS needed, it is the base of the rest of calculations
    const lastHandPrice = (this.state.bank * totalPrice) /
        (hands[hands.length - 1].bankPrice -
        (hands[hands.length - 1].commision / 100));

    const newHands = hands.reduceRight(
        // calculation starts in reverse, with the first value being lastHandPrice
        // it returns an array of number values reversed again to be in correct order
        (newHands, hand, ind, array) => {
            let prevHand = array[ind + 1];
            let prevPrice = newHands[array.length - ind - 2];
            if (hand == array[array.length - 1]) {
                return newHands.concat(lastHandPrice)
            } else {
                return newHands.concat(
                    prevPrice * (1 - (prevHand.commision / 100)) /
                    (hand.bankPrice - (hand.commision / 100))
                )
            }
        }, []
    ).reverse();


    if (this.state.hands !== prevState.hands)
        this.setState({
            totalPrice: totalPrice,
            hands: this.state.hands.map((hand, index) => {
                return Object.assign(hand, {realPrice: newHands[index]})
            })
        })
}

There are four main input fields in a div, represented in state.hands . div中有四个主要输入字段,以state.hands表示。 So this code works just perfect in my terminal but react throws me a Maximum update depth exceeded error when I try the following: 因此,此代码在我的终端中工作得非常完美,但是当我尝试以下操作时,反应会引发我Maximum update depth exceeded错误:

  • Add a new hand to the array 向数组添加新手

  • Try to enter anything in the input 尝试在输入中输入任何内容

There's nothing more I can really do about it. 我真的无能为力了。 I tried a few other methods to achieve this, but I always get the same error. 我尝试了其他几种方法来实现此目的,但始终会遇到相同的错误。 It seems like some kind of loop starts to work but I just can't figure it out. 似乎某种循环开始起作用,但我无法弄清楚。 Why it won't let me to update state.hands ? 为什么不让我更新state.hands

Input change/add/or remove handlers are very predictable, just updating the needed value, adding a div of four input fields, and removing the selected div. 输入更改/添加/或删除处理程序非常可预测,只需更新所需的值,添加四个输入字段的div,然后删除所选的div。 To change the value I map() over the state.hands and then Object.assign() the property value I need to the object I need (hand1, hand2, etc). 要在state.hands更改map() ,然后在Object.assign()更改所需的属性值,以更改所需的对象(hand1,hand2等)。

EDIT (the rest of the code): 编辑(其余代码):

handleInputChange = (event) => {
    const handName = event.currentTarget.parentNode.getAttribute("name");
    const handInput = event.currentTarget.getAttribute("name");
    const value = event.currentTarget.value;
    if (handInput === "bank" || handInput === "totalPrice") {
        this.setState({
            [handInput]: value
        });
    } else {
        this.setState(prevState => ({
            hands: prevState.hands.map(
            hand => {
                if (hand.name === handName) {
                    return Object.assign(hand, { [handInput]: value })
                } else {
                    return hand
                }
            }),
        }));
    }
}

handleAddHand = () => {
    const handCount = this.state.handCount;
    const name = `hand${handCount + 1}`;
    const hand = {
        name,
        backPrice: 0,
        bankPrice: 0,
        commision: 0,
        realPrice: 0
    };
    this.setState(prevState => ({
        hands: prevState.hands.concat({...hand}),
        handCount: prevState.handCount + 1
        })
    );
}

handleRemoveHand = (event) => {
    const handName = event.currentTarget.parentNode.getAttribute("name");
    const handIndex = this.state.hands.findIndex(hand => 
        hand.name === handName);
    this.setState(prevState => ({
        hands: [
            ...prevState.hands.slice(0, handIndex),
            ...prevState.hands.slice(handIndex + 1)
        ]
    }));
}
render() {
    const listHands = this.state.hands
        .map(hand =>
            <HandInputs
                key={hand.name}
                name={hand.name}
                backPrice={hand.backPrice}
                bankPrice={hand.bankPrice}
                commision={hand.commision}
                realPrice={hand.realPrice}
                handleChange={this.handleInputChange}
                removeHand={this.handleRemoveHand}
            />
    );

    return (
        <div>
            <input
                name="bank"
                value={this.state.bank}
                onChange={this.handleInputChange}
            />
            <input
                name="totalPrice"
                value={this.state.totalPrice}
                onChange={this.handleInputChange}
            />
            {listHands}
            <button onClick={this.handleAddHand}>Add Hand</button>
        </div>
    );
}


function HandInputs(props) {
    return (
        <div name={props.name}>
            <input type="text" value={props.backPrice}
              name="backPrice" onChange={props.handleChange} />
            <input type="text" value={props.bankPrice}
              name="bankPrice" onChange={props.handleChange} />
            <input type="text" value={props.commision}
              name="commision" onChange={props.handleChange} />
            <input type="text" value={props.realPrice}
              name="realPrice" onChange={props.handleChange} />
            {props.name !== "hand1" && 
              <button onClick={props.removeLeg}>Remove leg</button>}
        </div>
    );
}

Because you call setState in componentDidUpdate it makes the component go over the update life cycle again and call componentDidUpdate in a loop. 因为您在componentDidUpdate调用setState ,它使组件再次经过更新生命周期,并在循环中调用componentDidUpdate Your check: if (this.state.hands !== prevState.hands) will always be true because you always create a new hands array in state: 你的检查: if (this.state.hands !== prevState.hands)将永远是true ,因为你总是创建一个新的hands在状态数组:

this.setState({
   totalPrice: totalPrice,
   hands: this.state.hands.map((hand, index) => { <-- always cause a new array
      return Object.assign(hand, {realPrice: newHands[index]})
   })
})

One solution for this is to use the callback function as the second argument to setState . 一种解决方案是将callback函数用作setState的第二个参数。 This callback will be called after the change of the state and inside it you can use the state as you used it in componentDidUpdate . 状态更改后将调用此回调,并且可以在内部使用它,就像在componentDidUpdate使用state一样。

// the same function as your componentDidUpdate
afterStateChange = () => {
   const hands = this.state.hands;

   // initialized variables needed to calculate a result
   ...

   // you can call `setState` here (you don't need to check if the state is different from prevState
   this.setState({
       totalPrice: totalPrice,
       hands: this.state.hands.map((hand, index) => {
          return Object.assign(hand, { realPrice: newHands[index] });
       })
   });

And then for example you can use it here: 然后例如,您可以在这里使用它:

  handleAddHand = () => {
    const handCount = this.state.handCount;
    const name = `hand${handCount + 1}`;
    const hand = {
      name,
      backPrice: 0,
      bankPrice: 0,
      commision: 0,
      realPrice: 0
    };
    this.setState(
      prevState => ({
        hands: prevState.hands.concat({ ...hand }),
        handCount: prevState.handCount + 1
      }),
      this.afterStateChange <-- here
    );
  };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何根据 React Native 的 forms 中的字段值更新其他字段? - How to update other fields based on a field value in forms of React Native? 如何根据反应中该页面上一个字段上给出的数字动态显示多个输入字段 - How to dynamically display a number of input fields based on a number given on a previous field on that page in react 通过“输入”字段和下拉列表动态更新字段 - Dynamically Update fields through Input field and dropdown 如何使用jQuery动态基于其他输入框更新值? - How to update value based on other input box dynamically using jQuery? 如何在JavaScript,PHP响应的ajax中动态创建的输入字段中更新特定的输入字段? - How to update a particular input field among dynamically created input fields in JavaScript, ajax from PHP response? Django根据其他字段动态设置字段值 - Django set field values dynamically based on other fields 在 React 的特定输入字段中输入文本后,如何启用按钮? - How to enable a button once text is typed in a particular input field in React? React:根据动态生成的表单中的其他字段更新字段值 - React: update a field value based on another field in dynamically generated form 我怎样才能得到一个<select>字段根据来自另一个的输入动态更改<select>React 中的字段? - How can I get a <select> field to change dynamically based on the input from another <select> field in React? 根据键入字段的字符数动态扩展输入类型“文本”的高度 - Dynamically expand height of input type “text” based on number of characters typed into field
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM