React JS - общение между ребенком и родителем и двойные события

Я столкнулся с проблемой создания небольшого приложения для расчета цены некоторых продуктов в React. Вот так выглядит мое приложение:

Изображение 1

Теперь мне нужно иметь глобальную сумму (сумму частичной суммы компонентов ListItem), но я не знаю, как это сделать с помощью React. Я попытался использовать те же события "onChange" самого маленького компонента (ListItem), чтобы вызвать событие на родительском, например:

handleChange:function(event){

  this.props.onChange(event.target);

  const target = event.target;
  const name = target.name;
  const value = target.value;

  this.setState({
    [name]: value
  });
},

но таким образом только это событие было запущено и не обновило состояние. Возможно, я что-то упустил.

Вкратце, мне нужно передать частичную сумму, вычисленную в ListItem, в родительский компонент Table, чтобы я мог вычислить общую сумму.

function Header(){
  return (
    <h1>Calcolo costo prodotti</h1>
  )
}

var ListItem = React.createClass({
    getInitialState: function(){
      return {name: this.props.value.product.name, costo: this.props.value.product.costo, quantita: this.props.value.product.quantita, totale: 0} 
    },
  
    render: function(){
    return(
      <tr>
        <td><input type="text" name="name" value={this.state.name} onChange={this.handleChange} placeholder="Nome..."/></td>
        <td><input type="text" name="costo" value={this.state.costo} onChange={this.handleChange} placeholder="Costo unitario..."/></td>
        <td><input type="text" name="quantita" value={this.state.quantita} onChange={this.handleChange} placeholder="Quantità..."/></td>
        <td className="total">{this.calcoloTotale()}</td>
      </tr>
    )
  },
    
  handleChange:function(event){
    const target = event.target;
    const name = target.name;
    const value = target.value;
    
    this.setState({
      [name]: value
    });
  },
  
  calcoloTotale: function(){
    var Ltotale = this.state.costo * this.state.quantita;
    this.setState({totale: Ltotale});
    return Ltotale;
  }
});
  
  
var Table = React.createClass({
  getInitialState: function(){
    return { totale: 0 }
  },
  
  render: function(){
    return(
      <div>
        <table>
          <tr>
            <th>Nome</th>
            <th>Prezzo</th> 
            <th>Quantità</th>
            <th>Totale</th>
          </tr>
          {this.props.items.map((prodotto) =>
            <ListItem key={prodotto.id} value={prodotto}/>
          )}
        </table>
      </div>
    )
  }
});

var AddNewRow = React.createClass({
  render: function(){
    return(
      <div>
        <button onClick={this.props.onClick}>+</button>
        Aggiungi prodotto
      </div>
    )
  }
});

var Calculator = React.createClass({
  getInitialState: function(){
    return {
      counter: 2, lists: [{id: "0", product: {name: "Esempio 1",costo: "25",quantita: "3"}}, {id: "1", product: {name: "Esempio 2",costo: "32",quantita: "4"}}]
    }
  },
  
  render: function(){
    return (
      <div className="container">
        <Header />
        <Table items={this.state.lists} ids={this.counter}/>
        <AddNewRow onClick={this.addRow}/>
      </div>
    )
  },
  
  addRow: function(){
    this.setState({counter: this.state.counter + 1});
    var listItem = {id: this.state.counter, product:{name:"", costo: "", quantita: ""}};
    var allItem = this.state.lists.concat([listItem])
    this.setState({lists: allItem});
  }
});

ReactDOM.render(
  <Calculator />,
  document.body
);

РЕДАКТИРОВАТЬ 1:

var totalVec = new Array();

function Header(){
  return (
    <h1>Calcolo costo prodotti</h1>
  )
}

var ListItem = React.createClass({
    getInitialState: function(){
      return {name: this.props.value.product.name, costo: this.props.value.product.costo, quantita: this.props.value.product.quantita} 
    },
  
    render: function(){
    return(
      <tr>
        <td><input type="text" name="name" value={this.state.name} onChange={this.handleChange} placeholder="Nome..."/></td>
        <td><input type="text" name="costo" value={this.state.costo} onChange={this.handleChange} placeholder="Costo unitario..."/></td>
        <td><input type="text" name="quantita" value={this.state.quantita} onChange={this.handleChange} placeholder="Quantità..."/></td>
        <td className="total">{this.calcoloTotale()}</td>
      </tr>
    )
  },
    
  handleChange:function(event){
    const target = event.target;
    const name = target.name;
    const value = target.value;
    
    this.setState({
      [name]: value
    });
    
    this.props.updateGlobalTotal();
  },
  
  calcoloTotale: function(){
    var Ltotale = this.state.costo * this.state.quantita;
    totalVec[this.props.value.id] = Ltotale;
    return Ltotale;
  }
});
  
  
var Table = React.createClass({
  getInitialState: function(){
    return { totale: 0 } 
  },
  
  render: function(){
    return(
      <div>
        <table>
          <tr>
            <th>Nome</th>
            <th>Prezzo</th> 
            <th>Quantità</th>
            <th>Totale</th>
          </tr>
          {this.props.items.map((prodotto) =>
            <ListItem key={prodotto.id} value={prodotto} updateGlobalTotal={this.updateGlobalTotal}/>
          )}
        </table>
        <h1>{this.state.totale}</h1>
      </div>
    )
  },
  
  componentDidMount: function(){
    var total = 0;
    for(var i = 0; i < this.props.ids; i++){
      total += totalVec[i];
    }
    
    this.setState({totale: total});
  },
  
  updateGlobalTotal: function(){
    var total = 0;
    for(var i = 0; i < this.props.ids; i++){
      total += totalVec[i];
    }
    
    this.setState({totale: total});
  }
  
});

var AddNewRow = React.createClass({
  render: function(){
    return(
      <div>
        <button onClick={this.props.onClick}>+</button>
        Aggiungi prodotto
      </div>
    )
  }
});

var Calculator = React.createClass({
  getInitialState: function(){
    return {
      counter: 2, lists: [{id: "0", product: {name: "Esempio 1",costo: "25",quantita: "3"}}, {id: "1", product: {name: "Esempio 2",costo: "32",quantita: "4"}}]
    }
  },
  
  render: function(){
    return (
      <div className="container">
        <Header />
        <Table items={this.state.lists} ids={this.state.counter}/>
        <AddNewRow onClick={this.addRow}/>
      </div>
    )
  },
  
  addRow: function(){
    this.setState({counter: this.state.counter + 1});
    var listItem = {id: this.state.counter, product:{name:"", costo: "", quantita: ""}};
    var allItem = this.state.lists.concat([listItem])
    this.setState({lists: allItem});
  }
});

ReactDOM.render(
  <Calculator />,
  document.body
);

3 ответа

Решение

Вы можете добавить функцию в родительский объект, которая изменяет состояние этого компонента, а затем отправить эту функцию в качестве опоры дочернему элементу.

var ListItem = React.createClass({
    getInitialState: function(){
      return {name: this.props.value.product.name, costo: this.props.value.product.costo, quantita: this.props.value.product.quantita, totale: 0} 
    },
    
  ...
  
  calcoloTotale: function(){
    var Ltotale = this.state.costo * this.state.quantita;
    this.props.updateGlobalTotal(Ltotale);
    this.setState({totale: Ltotale});
    return Ltotale;
  }
});
  
  
var Table = React.createClass({
  getInitialState: function(){
    return { totale: 0 }
  },
  updateGlobalTotal: function(value){
      this.setState({totale: this.state.totale + value})
  }
  
  render: function(){
    return(
      <div>
        <table>
          <tr>
            <th>Nome</th>
            <th>Prezzo</th> 
            <th>Quantità</th>
            <th>Totale</th>
          </tr>
          {this.props.items.map((prodotto) =>
            <ListItem key={prodotto.id} value={prodotto} updateGlobalTotal={this.updateGlobalTotal}/>
          )}
        </table>
      </div>
    )
  }
});

Вы можете проверить Redux, как упомянул Ksyqo. Тем не менее, для таких нужд это может быть не совсем то, что вам нужно, так как это потребует от вас применения широкого спектра шаблонных и когнитивных накладных расходов в этот конкретный момент, когда у вас уже написан существующий код приложения.

Для небольших проектов можно обнаружить, что с лучшими результатами можно использовать альтернативу MobX, так как это немного проще реализовать, особенно в существующих приложениях. Это также намного легче рассуждать. Это будет работать в значительной степени из коробки, с небольшим количеством волшебства.

Каким бы ни было решение, этот график верен как для Redux, так и для MobX, и иллюстрирует проблему глобального состояния по сравнению с цепочечным состоянием родитель-потомок (первый, очевидно, намного чище):

введите описание изображения здесь

Я думаю, что Redux создан для вас.

Это помогает передавать свойства между компонентами, которые не обязательно имеют дочерние / родительские отношения. По сути, он создает глобальное состояние, к которому вы можете получить доступ из любого места.

О редуксе можно многое сказать (мы не называем его реакцией / редукцией без причины), и это необходимо, если вы хотите сделать что-то мощное с React.

Вы можете найти больше на документации Redux!

Другие вопросы по тегам