简体   繁体   中英

Refresh one child React component from form submit in sibling React component

I have a component called Bills, which embeds two child components, AddBill and DisplayBills.

AddBill is a managed webform which submits the fields as json to an API endpoint (using fetch post).

DisplayBills gets Bill data as json from an API endpoint and displays this in an HTML table (using fetch).

When I click the submit button on the webform in AddBill, I would like to submit the data to the database via the API endpoint, and then I would like to do a refresh of DisplayBills to show this bill is now in the database.

I've tried several ways of wiring this up but nothing seems to work.

The simplest way is to use a prop to inform the parent when a new bill is added, and add it to the bills state array which is passed into DisplayBills.

import React, { useState } from 'react';
import './styles.css';

function AddBill({ onAddBill }) {
  const [name, setName] = useState('');
  const [cost, setCost] = useState(0);

  function handleSubmit(e) {
    e.preventDefault();
    onAddBill({ name, cost });
    setName('');
    setCost(0);
  }

  return (
    <form className="add-bill" onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        placeholder="Bill name"
        onChange={e => setName(e.target.value)}
      />
      <input
        type="number"
        min="0"
        value={String(cost)}
        placeholder="Cost"
        onChange={e => setCost(Number(e.target.value))}
      />
      <button type="submit">Add</button>
    </form>
  );
}

function DisplayBills({ bills }) {
  if (!bills.length) {
    return <div>No bills yet!</div>;
  }

  return (
    <div className="bill-list">
      {bills.map(bill => (
        <div key={bill.name}>
          Bill {bill.name} cost ${bill.cost}
        </div>
      ))}
    </div>
  );
}

function Bills() {
  const [bills, setBills] = useState([]);

  function handleAddBill(bill) {
    setBills([bill, ...bills]);
  }

  return (
    <div className="bills">
      <AddBill onAddBill={handleAddBill} />
      <DisplayBills bills={bills} />
    </div>
  );
}

export default function App() {
  return (
    <div className="App">
      <Bills />
    </div>
  );
}

Sandbox: https://codesandbox.io/s/agitated-pine-o5c7g

Note that I have used the bill name as the unique key, so really I should be enforcing that it is unique, or else just creating a unique ID for each bill (arrays should have unique IDs for rendering in React).

As per comments if you're using class components your state will look more like this:

class Bills extends PureComponent {
  state = { bills: [] }

  handleBillAdd = bill => {
    this.setState({ bills: [bill, ...this.state.bills] });
  }
}

When you call your handleAddBill AND API call is successful, in your state save something like this lastCall: Date.now() and pass that piece of state to DisplayBills component as props <DisplayBills lastCall={this.state.lastCall} /> . Now every time you call handleAddBill function, DisplayBills component's props will be updated with a unique value. In your DisplayBills component's componentDidUpdate(prevProps, prevState) life-cycle method, check if prevProps.lastCall !== this.props.lastCall , if they are different, then make your new API call to fetch latest bills, if they are same, that means handleAddBill is not called successfully so you don't have to update your DisplayBills component.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM