Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rendering into multiple placeholder divs instead of a single root #355

Closed
davidlivingrooms opened this issue Apr 15, 2016 · 20 comments
Closed

Comments

@davidlivingrooms
Copy link

davidlivingrooms commented Apr 15, 2016

Most examples assume that all of your html markup will live in either the root component or one of its children like this.

   ReactDOM.render(
      <Provider store={store}>
        <MyRootComponent />
      </Provider>,
     rootEl
   )

I'm working on a project where we are parsing an html page and looking for a custom html tag for example

   <Component type="checkBox" id="blah" otherProp="someValue"> </Component>

If we find this tag we render a react component where this tag used to be. There can be many of these tags on an html page. This worked before no problem since we could just call render once per tag and it would replace it. Just to be clear, the entire page is NOT a react app we only render react components into these special placeholder tags and the rest of the page is just regular html.

Is it possible to do the same with react-redux? All the examples I have seen contain a single render at the highest level that has the provider as a wrapper. How could I accomplish multiple renders into specific placeholder tags but still benefit from a redux global store? I've looked into dangerouslySetInnerHtml but it seems to be frowned upon for security reasons. It also doesn't really accomplish what I want since it ends up rendering these placeholder tags directly into the html when what I really want to do is replace each one of these tags with a react component render. I've been stuck on this for awhile so any and all help would really be appreciated. Thank you.

@anatoliyarkhipov
Copy link

Provider is very simple. It just passes the given store to the context. So you can have multiple providers on a page. Just give them the same instance of store.

const store = createStore(/* ... */)

// ...

ReactDOM.render(
    <Provider store={store}>
        <CheckBox />
    </Provider>,
    checkBoxTargetNode
)

ReactDOM.render(
    <Provider store={store}>
        <TextBox />
    </Provider>,
    textBoxTargetNode
)

@markerikson
Copy link
Contributor

Yep, that should work. @GrumpyPants , if you have more concerns beyond that let us know, but yeah, simply rendering multiple providers with the same store reference should do exactly what you want.

@davidlivingrooms
Copy link
Author

This is great to hear. I started looking for workarounds before even trying to do the multiple render so that's on me. Sorry for the time-waste and thanks for the help :)

@markerikson
Copy link
Contributor

Sure. Usage questions are generally better off on Stack Overflow rather than here, but it is admittedly a more niche use case you were asking about.

@toddgroff
Copy link

toddgroff commented Sep 29, 2016

@GrumpyPants Do you have a public repo that shows how you implemented rendering of multiple <Provider> components for a shared store? More specifically, is there any way you've found to do this while still creating container components via { connect }?

Feel free to email me at todd.groff@gmail.com as this is not so much a bug as a use case question like Mark stated.

@markerikson
Copy link
Contributor

@toddgroff : the example that @anatoliyarkhipov gave should be the basic idea. Create one store instance, then call ReactDOM.render() multiple times with your different component trees.

@ghost
Copy link

ghost commented Oct 17, 2016

Hi I'm having the same issue I have 2 ReactDom.render() but for whatever reason when I do an action on component (A) component (B) does not update. What I'm trying to do is just turn a value from true to false. When I change the value, Component (A) reacts to it and updates but Component (B) stays the same.

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import ReduxPromise from 'redux-promise';
import App from './components/app';
import SideImageComp from './components/SideImage/SideImageComp';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
  <App/>
</Provider>, document.querySelector('.Approot'));


ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
  <SideImageComp/>
</Provider>, document.querySelector('.sideImage'));

@jimbolla
Copy link
Contributor

@jaysg Looks like you're creating 2 instances of your store instead of just one.

@ghost
Copy link

ghost commented Oct 17, 2016

@jimbolla thanks thats what it was. I created a variable for the store and now it works.

just in case someone one sees this and it's going through the same thing.

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import ReduxPromise from 'redux-promise';
import App from './components/app';
import SideImageComp from './components/SideImage/SideImageComp';
import reducers from './reducers';

const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);


// This is what I added
let newstore = createStoreWithMiddleware(reducers);

ReactDOM.render(
  <Provider store={newstore}>
  <App/>
</Provider>, document.querySelector('.Approot'));


ReactDOM.render(
  <Provider store={newstore}>
  <SideImageComp/>
</Provider>, document.querySelector('.sideImage'));

@mattkoch614
Copy link

Provider is very simple. It just passes the given store to the context. So you can have multiple providers on a page. Just give them the same instance of store.

const store = createStore(/* ... */)

// ...

ReactDOM.render(
    <Provider store={store}>
        <CheckBox />
    </Provider>,
    checkBoxTargetNode
)

ReactDOM.render(
    <Provider store={store}>
        <TextBox />
    </Provider>,
    textBoxTargetNode
)

Is this still an OK approach in 2019?

@markerikson
Copy link
Contributor

@mattkoch614 : yes, nothing about that concept has changed at all.

@o-julio
Copy link

o-julio commented Jun 18, 2019

This approach is getting me a Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.

Any ideas?

@Lukavyi
Copy link

Lukavyi commented Apr 2, 2020

This approach is getting me a Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.

Any ideas?

Same error for me, when I try to render lots of components on page wrapped inside web components.

@markerikson
Copy link
Contributor

Can you create a CodeSandbox that demonstrates the issue?

@Lukavyi
Copy link

Lukavyi commented Apr 2, 2020

Found a reason, I was rendering react components wrapped in web components from my external package both with connected react components inside one app.

@joel1st
Copy link

joel1st commented Aug 11, 2021

@markerikson

Can you create a CodeSandbox that demonstrates the issue?

https://codesandbox.io/s/react-pixi-fiber-with-redux-forked-bcel4?file=/src/App.js

To see the warning console message click the '+' or '-' button next to the count text.

In this example there are three providers rendered. It is not an issue when there are only two providers. The three different renderers in use are React-dom/React-Three-Fiber/React-Pixi. Commenting out the Canvas block prevents the issue, as there is now only two renderers, same goes with the Stage block.

@markerikson
Copy link
Contributor

@joel1st : I believe that's a known issue with how React handles context instances.

React has some concept of "primary" and "secondary" renderers. See facebook/react#19519 (comment) for a description.

That message gets printed when multiple renderers of some kind are using the exact same context instance, and isn't specific to React-Redux - it's a React thing.

@joel1st
Copy link

joel1st commented Aug 11, 2021

@markerikson - thanks for the quick reply and the helpful information. That is quite an annoying limitation for this admittedly niche use case.

I haven't had any luck determining a work around, but my thought is to just pass redux data as props through to one of the two renderers (and a dispatch callback prop). Can you think of a more redux-y approach?, or does that sound about right?

@markerikson
Copy link
Contributor

markerikson commented Aug 11, 2021

You might want to try using the useContextBridge hook from https://github.com/pmndrs/drei#usecontextbridge. Not sure if that requires rendering another <Provider> inside the second renderer boundary or not. Also related, see https://standard.ai/blog/introducing-standard-view-and-react-three-fiber-context-bridge/ .

@joel1st
Copy link

joel1st commented Aug 11, 2021

@markerikson Thanks for the suggestion. Unfortunately this appears to just be a utility to prevent manually nesting the contexts yourself. Behind the scenes, it is still rendering the same context provider and the console warning still persists.

For now I will just pass the data via props to whichever renderer has the most shallow render tree :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants