1

I have this app that uses mobx, in it there is a component called "Listings" that uses some state from mobx to render a list of items.

The way it is right now, is that the Listings component gets the data it needs(store.restaurantResults[store.selectedFood]) from inside of it by using the mobx store like so:

const Listings = () => {
  const store = React.useContext(StoreContext);

  return useObserver(() => (
    <div className="pa2">
        {store.restaurantResults[store.selectedFood] &&
          store.restaurantResults[store.selectedFood].map((rest, i) => {
            return (
              <div key={i} className="pa2 listing">
                <p>{rest.name}</p>
              </div>
            );
          })}
    </div>
  ));
};

But i think this is wrong, as it couples the component with the data, I want instead to pass that data via props so it can be reusable.

What is the correct way to do this? Right now my App looks like this, where it's being wrapped around a storeProvider:

function App() {
  return (
    <StoreProvider>
      <div className="mw8 center">
        <Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way"/> 
        <FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
        <p className="b tc pt3">or...</p>
        <Search /> 
        <Listings />
      </div>
    </StoreProvider>
  );
}

My idea is to extract everrything inside the StoreProvider into another component that has a store and returns the jsx via useObserver so that I can acces the store and then pass what i need as props to the other components. like this:

const Wapper = () => {
  const store = React.useContext(StoreContext);
  return useObserver(() => (
    <div className="mw8 center">
      <Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way" />
      <FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
      <p className="b tc pt3">or...</p>
      <Search />
      <Listings listings={store.restaurantResults[store.selectedFood]} />
    </div>
  ))
}

And then on the listings component change the hard coded store.restaurantResults[store.selectedFood] inside to use the props that is being passes now, that is called listigs like so:

const Listings = ({listings}) => {
  const store = React.useContext(StoreContext);

  return useObserver(() => (
    store.loading
      ? <Loading />
      : <div className="pa2">
        <div className="flex flex-wrap">
          {listings &&
            listings.map((rest, i) => {
              return (
                <div key={i} className="pa2 listing">
                  <img className='object-fit' src={rest.image_url} alt="restuarant" />
                  <p>{rest.name}</p>
                  <p>{rest.location.address1}</p>
                </div>
              );
            })}
        </div>
      </div>
  ));
};

And this works, but is this the right way to go about this?

  • still depends directly on `store` for `loading`, can be passed as prop, too – xadm Feb 10 '20 at 20:25
  • but if im passing the whole store i could do store.loading.... but that still doesnt work bc store is always undefined –  Feb 10 '20 at 22:57
  • sorry, but it improves nothing ... instead one specialised `` you're creating more reusable (?) the same ... and increases Wrapping Hell ... doesn't make sense for mobx and context usage optimization (minimize rerenderings) – xadm Feb 10 '20 at 23:02
  • you can just pass `loading={store.loading}` and use `loading` inside – xadm Feb 10 '20 at 23:13
  • https://stackoverflow.com/a/50817822/6124657 – xadm Feb 10 '20 at 23:22

2 Answers2

0

As <Listings/> can be provided with listing and loading you can:

const Listings = ({listings, loading}) => {
  if(loading) return <Loading />
  return (
    <div className="pa2">
      <div className="flex flex-wrap">
        {listings && listings.map((rest, i) => {
          return (
            <div key={i} className="pa2 listing">
              <img className='object-fit' src={rest.image_url} alt="restuarant" />
              <p>{rest.name}</p>
              <p>{rest.location.address1}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
}

No observables used, no useObservable required.

You want to useObservables on store for listings then no reason to wrap all components with useObservable. You should wrap <Listings/> only.

xadm
  • 8,219
  • 3
  • 14
  • 25
  • a ha! thats the thing.. i must pass them as individual props, already converted into values.. not the whole store..thx! –  Feb 11 '20 at 10:48
  • ... you can use graphql, `useQuery` etc. instead all readux/mobx ;) – xadm Feb 11 '20 at 11:30
-1

I usually define my store as a global, so every component has visibility of it:

class Store {
  @observable myVar
}

global.store = new Store()

And in my components i just use it:

@observer
export default class MyComponent extends React.Component {
  constructor () {
    super()
    store.myVar = 0
  }

  setMyVar (a) {
    store.myVar += 1
  }

  render () {
    return <button onClick={this.setMyVar}>
      Clicked {store.myVar} times
    </button>
  }
}
Tomás Lima
  • 313
  • 2
  • 9
  • globals should be avoided in js for many reasons (search/google), using context is a react recommended solution for that – xadm Feb 11 '20 at 00:52