Home > front end >  React: Update variable in different components on event
React: Update variable in different components on event

Time:08-01

I'm fairly new to react and I'm probably using a wrong concept since I'm trying to solve a simple problem.

Imagine a parent component with multiple children:

  • OverviewComponent (parent)
  • DetailsComponent (child)
  • EditComponent (child)
  • HistoryComponent (child)

The parent holds all three children. The data displayed in the three children is the same object.

What I'm trying to do is to update the data in the children based on a click in the HistoryComponent. Think of an list of objects inside the HistoryComponents. When a user clicks on a list entry, the DetailsComponent should show data based on the object selected inside the HistoryComponent. Same goes for the EditComponent.

I've got two ideas to solve this:

  1. Pass a prop with the objectsId from the parent to all children and if the user selects a different object in the HistoryComponent, use a function which gets also passed as prop to update the props.objectId inside the OverviewComponent (parent) --> Problem: Code looks ugly and I can't get it to work
  2. Use useState and useEffect. --> Problem: Due to the async nature of useState, this won't update the property immediatly in all child components.

How do I solve this problem? This seems like a very trivial problem/concept most react apps use, so this can't be that complicated, huh?

Any help is appreciated!

CodePudding user response:

sometimes is better to start off with a working example. here's an implementation of what I think is your concept - just 3 components: Overview, History & Details. Currently selected item is held in the state selected and setSelected is passed as a prop to the History component to adjust selected as user clicks an item.

import React from 'react';
import ReactDOM from 'react-dom/client';
import {useState} from "react";

const history = [
    {
        id: 0,
        name: 'entry 0',
        details: 'details for entry 0'
    },
    {
        id: 1,
        name: 'entry 1',
        details: 'details for entry 1'
    },
    {
        id: 2,
        name: 'entry 2',
        details: 'details for entry 2'
    }
]

function Details({item}) {
    return (
        item && <div>
            <p>item details:</p>
            <h2>{item.name}</h2>
            <p>{item.details}</p>
        </div>
    );
}

function History({items, setSelected}) {

    const handleChange = (e) => {
        setSelected(e.target.value);
    }

    return (
        <div>
            <label htmlFor="history">History</label>
            <br/>
            <select id="history" name="history" size={4} onChange={handleChange}>
                {items.map(e => <option value={e.id} key={e.id}>{e.name}</option>)}
            </select>
        </div>
    )
}

function Overview({items}) {

    const [selected, setSelected] = useState();

    return(
        <div>
            <p>selected entry: {selected ?? 'none'}</p>
            <History items={items} setSelected={setSelected}/>
            <Details item={selected ? items[selected] : null}/>
        </div>
    )
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <Overview items={history}/>
    </React.StrictMode>
);

somewhere in you index.html you have to put <div id="root"></div> as an anchor point for your React app

CodePudding user response:

What you mentioned can be easily done by useContext hook. First create a context :

export const MyListContext = createContext();

Then In OverviewComponent use that context:

function OverviewComponent() {
  const [myList, setMyList] = useState([]);
  return ( 
    <MyListContext.Provider value={{myList, setMyList}}>
      <DetailsComponent />
      <EditComponent />
      <HistoryComponent />
    </MyListContext.Provider>
  );
}

Ps: myList and setMyList can be accessible in any component which is wrapped into MyListContext.Provider. If each one of them changes myList value (using setMyList), then every one gets updated.

You can use the defined context in child components like so:

function HistoryComponent() {
  const { myList, setMyList } = useContext(MyListContext);
  return (
    <button onClick={()=>setMyList(["this is an example."])}>updateList</button>
  )
}
  • Related