Home > Blockchain >  Is there another way to pass/store the props that doesn't feel like a bodge?
Is there another way to pass/store the props that doesn't feel like a bodge?

Time:12-16

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
    
    
}
  • Related