I have to pass/store data in my ReactTS App. Everything works, but it doesnt feel right.
I have 4 Components and they pass meal
around.
meal
comes from another component to MealDetails
(the component needs it to work). It passes meal
to AddFood
(bec it has to jump back to MealDetails
at one point - so it needs meal
).
An important function is in BarcodeScannerPluginReworks
so this needs meal
aswell through BarcodeScannerRework
, because this one jumps back to AddFood
.
So im sending meal
in a circle. Is there a way to save it in MealDetails
and only pass a function to AddFood
to change some values in meal
instead of sending it full circle.
MealDetails.tsx
const MealDetails = () => {
const location = useLocation();
const navigate = useNavigate();
const { meal } = location.state || {};
return (
<div className="MealDetails">
<Link className="Link" to={`/AddFood/${meal.id}`} state={{ meal: meal }}>
<div className="MealDetailsAddFoodButton">
<img className="SVG" src={SVG} alt=" 21" />
</div>
</Link>
</div>
);
AddFood.tsx
const AddFood = () => {
const navigate = useNavigate();
const location = useLocation();
const mealId = useParams();
const { meal } = location.state || {};
return (
<div className="AddFood">
<header className="AddFoodHeader">
<div className="BackArrow-Meal">
<ArrowBackIcon
onClick={() => {
navigate(`/MealDetails/${mealId.id}`, { state: { meal : meal } });
}}
sx={{ fontSize: 35 }}
></ArrowBackIcon>
</div>
<div className="AddFoodSearchBar">
<Link className="Link" to={`/BarcodeScanner/`} state={{ meal : meal }} >
<CropFreeIcon className="QRIcon"sx={{ fontSize: 30 }}></CropFreeIcon>
</Link>
</div>
</header>
</div>
);
};
BarcodeScannerRework.jsx
const BarcodeScannerRework = () => {
const navigate = useNavigate();
const location = useLocation();
const { meal } = location.state;
return (
<div className="BarcodeScannerRework">
<div className="BarcodeScannerReworkPlugin">
<BarcodeScannerPluginRework meal={meal}/>
</div>
</div>
);
};
BarcodeScannerPluginRework.jsx
const BarcodeScannerPluginRework = ({meal}) => {
const navigate = useNavigate();
let html5QrCode;
useEffect(() => { //I've shortened this part a lot - it navigates when there is a successful response
navigate(`/AddFood/${meal.id}`, { state: { eanCodeFromCamera : decodedText , meal : meal}
});
return <div id={qrcodeId}></div>;
};
CodePudding user response:
There are different solutions possible when you have many components that need to share a state.
1. Using props and a common parent
The most common method is set the state on a common parent and then pass it as a prop. But it's not always possible if your components are all over the place or if you have a lot of nested components.
2. Redux
Redux is an external library which give you the possibility to have a global state (a store) for your app. It lets your components read data from the store, and dispatch actions to the store to update the state.
3. Context
React's Context API is a built-in solution which also allow you to share data for a tree of components. Given your question, it might be the best solution for you. Here is a basic example on how to implement it:
import { createContext, useContext } from "react";
const MealContext = createContext();
const MealProvider = ({ children }) => {
const [meal, setMeal] = useState(/* Initial meal value */);
const addFood = (food) => {
// Do something with meal...
};
return (
<MealContext.Provider
value={{
addFood,
meal
}}
>
{children}
</MealContext.Provider>
);
};
const useMeal = () => useContext(MealContext); // Custom hook to use the context
export { MealProvider, useMeal };
You can then set the provider where you need to manage meal
:
<MealProvider>
<MealDetails />
<AddFood />
</MealProvider>
And on your component:
const AddFood = () => {
const { meal, addFood } = useMeal();
return (/* ... */);
}
Please read the documentation carefully to understand better how to use the Context API. I also recommend this article, which explain how the rendering behavior of Context works.
CodePudding user response:
I build a globalstate
library react-global-state-management It is much easier to work with than context or redux
import GlobalState from 'react-global-state-management';
const data = GlobalState({
meal:...,
addFood:()=> {}
...
});
/ component 1
()=> {
data.meal =// something
return ()
}
/// component 2
()=> {
// bind a hook so when meal changes the component gets updated
data.hook(x=> [x.meal])
data.subscripe((item, prop)=> {
/// meal has changed
// use this togather with or without hook
}, x=> [x.meal])
data.addFood(...)
return //something
}