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

时间:2018-11-03 09:47:14

标签: javascript reactjs

因此,我遇到了这个问题,在过去的几天里,我的脑袋一直碰壁,那里有一个简单的表格,可能有多个div和四个输入,但它们彼此相关。我想做的是计算div值中的最后一个输入,该值基于其余输入的值并被动态键入,然后输入setState()以返回新状态。我有处理程序功能(用于添加,删除或更改输入字段的值),该功能在我不需要处理此特定更新时可以工作。现在是代码:

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]})
            })
        })
}

div中有四个主要输入字段,以state.hands表示。 因此,这段代码在我的终端机上工作得很完美,但是当我尝试以下操作时,响应会引发Maximum update depth exceeded错误:

  • 向数组添加新手

  • 尝试在输入中输入任何内容

我对此无能为力。我尝试了其他几种方法来实现此目的,但始终会遇到相同的错误。似乎某种循环开始起作用,但我无法弄清楚。为什么不让我更新state.hands

输入更改/添加/或删除处理程序非常可预测,只需更新所需的值,添加四个输入字段的div,然后删除所选的div。要在map()上更改值state.hands,然后再将Object.assign()的属性值更改为所需的对象(hand1,hand2等)。

编辑(其余代码):

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>
    );
}

1 个答案:

答案 0 :(得分:0)

因为您在setState中调用了componentDidUpdate,所以该组件再次经历了更新生命周期,并循环调用了componentDidUpdate。 您的支票: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]})
   })
})

一个解决方案是使用callback函数作为setState的第二个参数。状态更改后将调用此回调,并且您可以在其中使用state,就像在componentDidUpdate中使用它一样。

// 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] });
       })
   });

然后例如您可以在这里使用它:

  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
    );
  };