1

I followed the React-Router Modal tutorial to have something look-like 2 DOMs (one for the modal with current location, and the default with previous location).

The problem: I need to be able to pass through all Components into the Modal a new props isModal=true but I don't want to pass it for every children and sub-children components.

My Solution??: Use React-Redux to pass isModal props only for Modal's page. (UPDATE:Look like it's not possible with Redux, do I have another solution)

I know from the Redux-Provider that I can have only one store, but can I handle multiples Provider and store children for different component ?

index.js

ReactDOM.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>,
  document.getElementById('root')
);

App.js

render() {
  return [
    <Layout key="document-layout" {...this.props}>
      <Page {...{ ...this.props, location: isModal ? previousLocation : location}} />
    </Layout>,
    isModal
    ? <ModalLayout key="modal-layout">
        <Provider store={{...store, isModal:true}}>
          <Page {...this.props} />
        </Provider>
      </ModalLayout>
    : null
  ];
}

But on Modal's pages, on mapStateToProps state.isModal is never defined :/

Arthur
  • 4,870
  • 3
  • 32
  • 57
  • I just saw that `store` contain a Redux object (with dispatch, getState, ..) and not the final `state` available on with the `connect()` function.. So my code cannot works that way. Do I have a solution to do it ? – Arthur Aug 05 '19 at 00:01

1 Answers1

0

So if you want to use some global type state then I would use the new Context in the latest react like so...

...
React.useContext(SomeContext)
...

Read more here how you can use it and achieve a redux style app with it if you like.

Its a good read BUT STOP ....!!!!!!!!

I would not use that either to do what you want. Really your url is(should) be the global state and it should contain everything you need to show sometimes that means refactoring how your urls and pages are structured. In this example they pass model true though a state var, I try to avoid passing state though url link calls because if the user then refreshes the page on that url the state is lost and in this example there is a clear bug when doing that. So this is how i would achieve the same thing but using the url as king.

import React, { Component } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

class ModalSwitch extends Component {

  render() {
    return (
      <div>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/gallery/:type?/:id?" component={Gallery} />
          <Route path="/featured/img/:id" component={ImageView} />
        </Switch>
        <Route path="/gallery/img/:id" component={Modal} />
      </div>
    );
  }
}

const IMAGES = [
  { id: 0, title: "Dark Orchid", color: "DarkOrchid" },
  { id: 1, title: "Lime Green", color: "LimeGreen" },
  { id: 2, title: "Tomato", color: "Tomato" },
  { id: 3, title: "Seven Ate Nine", color: "#789" },
  { id: 4, title: "Crimson", color: "Crimson" }
];

function Thumbnail({ color }) {
  return (
    <div
      style={{
        width: 50,
        height: 50,
        background: color
      }}
    />
  );
}

function Image({ color }) {
  return (
    <div
      style={{
        width: "100%",
        height: 400,
        background: color
      }}
    />
  );
}

function Home() {
  return (
    <div>
      <Link to="/gallery">Visit the Gallery</Link>
      <h2>Featured Images</h2>
      <ul>
        <li>
          <Link to="/featured/img/2">Tomato</Link>
        </li>
        <li>
          <Link to="/featured/img/4">Crimson</Link>
        </li>
      </ul>
    </div>
  );
}

function Gallery() {
  return (
    <div>
      {IMAGES.map(i => (
        <Link
          key={i.id}
          to={{
            pathname: `/gallery/img/${i.id}`
          }}
        >
          <Thumbnail color={i.color} />
          <p>{i.title}</p>
        </Link>
      ))}
    </div>
  );
}

function ImageView({ match }) {
  let image = IMAGES[parseInt(match.params.id, 10)];

  if (!image) return <div>Image not found</div>;

  return (
    <div>
      <h1>{image.title}</h1>
      <Image color={image.color} />
    </div>
  );
}

function Modal({ match, history }) {
  let image = IMAGES[parseInt(match.params.id, 10)];

  if (!image) return null;

  let back = e => {
    e.stopPropagation();
    history.goBack();
  };

  return (
    <div
      onClick={back}
      style={{
        position: "absolute",
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
        background: "rgba(0, 0, 0, 0.15)"
      }}
    >
      <div
        className="modal"
        style={{
          position: "absolute",
          background: "#fff",
          top: 25,
          left: "10%",
          right: "10%",
          padding: 15,
          border: "2px solid #444"
        }}
      >
        <h1>{image.title}</h1>
        <Image color={image.color} />
        <button type="button" onClick={back}>
          Close
        </button>
      </div>
    </div>
  );
}

function ModalGallery() {
  return (
    <Router>
      <Route component={ModalSwitch} />
    </Router>
  );
}

export default ModalGallery;

This is much simpler way. The key here is using variables in the Route path.

When we visit /gallery we satisfy the Gallery path as we have said that the :type and :id are optional for this view.

So when we add the /gallery/img/0 we saying that this is a model as we want to lay it over the gallery and so we show both the Gallery and Modal components together.

But when we go to the /featured/img/0 it will only satisfy the ImageView

<Switch>
   <Route exact path="/" component={Home} />
   <Route path="/gallery/:type?/:id?" component={Gallery} />
   <Route path="/featured/img/:id" component={ImageView} />
</Switch>
<Route path="/gallery/img/:id" component={Modal} />
Wyck
  • 10,311
  • 6
  • 39
  • 60
Ben Smith
  • 111
  • 4
  • I understand your point but you're not answering my question you just remove the point. I know that 'url' should be related to the current state. But like Fb gallery i want to be able to make the difference between "asked on modal" or "new url page". Example: I'm on home `/`, and I want to display `/login` on modal. I'll display `/` on background as previous, and `/login` into the modal as current. If the user refresh `/login` is now current (and not required as modal), so display directly on the default layout as current url. – Arthur Aug 05 '19 at 15:41
  • Or the official React Router example is wrong? Because this is what they're doing :/ – Arthur Aug 05 '19 at 15:44
  • Also note that every url have to works on modal and on default layout, so with you solution I have to double all my routes. – Arthur Aug 05 '19 at 15:48
  • 1
    ok but redux is still not a good solution to that problem also as you have found that you can not have a provider within a provider. You can have many react contexts so maybe that is the correct solution in this case. – Ben Smith Aug 05 '19 at 17:26