How do you combine/compose multiple Higher Order Components (react.js)? - javascript

I've been looking into the concept of HOC's, but I'm not sure how to have multiple without just having an ugly deep nesting.
For example, if I wanted to add a library's HOC, like Redux provider or Apollo provider, then have my own HOC's and so on. Like below, is this not just really inefficient and ugly?
(Just an example, not actually using doing this at the moment. All theoretical.)
ReactDOM.render(
<ApolloProvider client={ client }>
<ReduxProvider state={ state }>
<MyHOC xyz={ abc }>
<App />
</MyHOC>
</ReduxProvider>
</ApolloProvider>,
document.getElementById('root')
)
As compared to
ReactDOM.render(
<CombinedHOCs client={ client } state={ state } xyz={ abc }>
<App />
</CombinedHOCs>,
document.getElementById('root')
)
But the only way I can think of this is looping through supplied HOC's which would again still have to loop (e.g. map) through everything (depending on what HOC does).
So am I looking at this the wrong way or is there no way to avoid this, assuming that those HOC's would be needed for the given component?

I think it's ok to nest HOC like you did in the example. But if you're using redux and Apollo you should integrate it and keep data in one store. You'll get rid of one nesting and this will let you better track the different events that happen in your app.
ReactDOM.render(
<ApolloProvider store={store} client={client}>
<MyHOC xyz={abc}>
<App />
</MyHOC>
</ApolloProvider>,
document.getElementById('root')
)

Related

React pass x state variables to y {props.children}

I'm using functional components. I have built my separate components and am placing them into my index.js file.
Right now the structure of the components is (pseudocode):
<App stateVariable1={x} stateVariable2={y} stateVariable3={z}>
<ChildOne props1={x} props2={y} />
<ChildTwo props1={z}/>
</App>
ChildOne and ChildTwo are currently rendered within App.js, and so passing the state variables to the two children is very easy.
I'd like to extract the rendered children into index.js and replace the two children with a {props.children} within App.js, to increase the SRP of the component and make it more reusable.
However I am struggling with understanding how to pass multiple state variables as multiple props to props.children within my index.js file.
I have read the react documentation for Render Props and that deals with one prop being passed to a single child, rather than multiple children which want to receive different props.
How do I go about achieving what I would like to do?
UPDATE
I have attempted to use render props as per the accepted answer here: How to pass props to {this.props.children}
My index.js looks like this:
ReactDOM.render(
<React.StrictMode>
<App>
{(stateVariable1) => (
<Fragment>
<ChildOne props1={stateVariable1} props2={stateVariable2} />
<ChildTwo props3={stateVariable3} />
</Fragment>
)}
</App>{' '}
</React.StrictMode>,
document.getElementById('root')
);
However all of the props/stateVariables through undefined errors, as of course they're not defined in index.js.
Where am I going wrong?
Update 2: Solution
I passed all of the state variables as arguments to the render props and this has solved the issue:
ReactDOM.render(
<React.StrictMode>
<App>
{(stateVariable1, stateVariable2, stateVariable3) => (
<Fragment>
<ChildOne props1={stateVariable1} props2={stateVariable2} />
<ChildTwo props3={stateVariable3} />
</Fragment>
)}
</App>{' '}
</React.StrictMode>,
document.getElementById('root')
);
Is there a way to deconstruct this so that I don't need to pass each argument into the callback? I am using functional components so state is stored in multiple variables rather than within the class components' state object.
I think you can avoid having to use props.children altogether.
If you have a structure like below, your SRP principle will still hold true.
App
Child 1
Child 2
What the above lines mean is that rather than going with render props pattern or having props.children, you can have your Child1 and Child2 as components within your App component.
Something like
const App = (props) => {
return (
<>
<Child1 {...props} />
<Child2 {...props} />
</>
);
}
If you want to make code more generic and have a single Children component, you can extract all children out to index.js file so that your App component remains untouched, but that might not be needed to be honest.
I solved my issue with some help from Tyblitz.
ReactDOM.render(
<React.StrictMode>
<App>
{({stateVar1, stateVar2, ...stateVars}) => (
<Fragment>
<ChildOne props1={stateVar1} props2={stateVar2} />
<ChildTwo {...stateVars} />
</Fragment>
)}
</App>{' '}
</React.StrictMode>,
document.getElementById('root')
);
I then added a useState() function to my App component and passed all state variables in as an object.
Finally, I passed this new state variable in as an argument to my return call:
return <div className='App'>{props.children(stateVars)}</div>;

Is it enough to use Provider one time?

In react-native when we use redux module we use createStore from 'redux'. And My question: is it enough to use <Provider/> one time which makes the Redux store available to the rest of our app.
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './store'
import App from './App'
const rootElement = document.getElementById('root')
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
)
Or we must also add <Provider/> somewhere?
Once you create a store with redux with the reducers for example just like below:
const store = createStore(reducers, applyMiddleware(thunk));
Then you need to use <Provider> wrapper only once:
ReactDOM.render(
<Provider store={store}>
<App />
<Provider>, document.getElementById('root')
);
Later if you want to access any of the object from the store you need to use mapStateToProps in your components. If any modification is needed you need to create actions and dispatch them with mapDispatchToProps.
From the the documentation:
The option we recommend is to use a special React Redux component called to magically make the store available to all container components in the application without passing it explicitly. You only need to use it once when you render the root component.
Hope this helps.
Yep, you only need to use Provider once to wrap your whole application.
The question looks interesting. You need to understand how the store is available to every component wrapped inside the <Provider>.
When you wrap any React component inside <Provider store={store}> every component will have access to something called context, through which it is possible to share the data even it's not an immediate child.
Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree. https://reactjs.org/docs/context.html
As it states that context can be used in any child component without explicitly passing a prop.
The <Provider> exactly works the same way. And when you use the redux the <Provider> sets the context. And you need to use the connect() from the react-redux it uses the context set by the <Provider> to pass properties to your component wrapped in connect() HOC.
Hence you need to use the <Provider> only once in your application.

Loading Stores in React-Redux

I am new to React-Redux, I have a app having all stores preloaded on the index.js file which is the entry point that renders the app . I have the following code in the page (a part of it).
if(Auth.isUserAuthenticated()){
store.dispatch(getProductCategories());
store.dispatch(getProducts());
store.dispatch(getAuctions());
store.dispatch(getAppSettings());
store.dispatch(getWalletTransactionHistory());
store.dispatch(getAllUsers());
store.dispatch(getParticipatedAuctions());
store.dispatch(getParticipatedAuctionsList());
store.dispatch(getWalletBalance())
}
store.dispatch(getHomePageAuctions());
store.dispatch(getAllPageAuctions());
store.dispatch(getPriceCards());
render(
<AppContainer>
<App store={store} history={history} />
</AppContainer>,
document.getElementById('app')
);
The action in the above are dispatched on every route change, But i Wanted it to dispatch only once on the page load . I even tried to put it on App component in ComponentDidMount() and also tried in putting it in constructor() of that component but no luck didn't work as expected and also searched google but no luck with exact match or similar to it
I suggest moving your store.dispatch calls inside App.js' componentDidMount. Since App.js will only mount once, they'll only fire once.

Apollo + React Router 4 SSR issue

Not sure if this is an issue with React Router v4, the React Apollo client or my implementation.
But with <ApolloProvider> as the top-level HOC, i.e:
const ComponentsWithData = await getDataFromTree(
<ApolloProvider client={apolloClient}>
<StaticRouter location={ctx.request.url} context={route}>
<App />
</StaticRouter>,
</ApolloProvider>,
);
const html = ReactDOMServer.renderToString(ComponentsWithData);
... I get:
Warning: Failed prop type: Invalid prop children of type array supplied to ApolloProvider, expected a single ReactElement.
in ApolloProvider
Error React.Children.only expected to receive a single React element child.
And flipped around, with React Router's <StaticRouter> as the top, i.e.:
const ComponentsWithData = await getDataFromTree(
<ApolloProvider client={apolloClient}>
<StaticRouter location={ctx.request.url} context={route}>
<App />
</StaticRouter>,
</ApolloProvider>,
);
... I then get:
A <Router> may have only one child element
Rendering works fine in the browser (with React Router's <BrowserRouter>), but fails on the server.
It also works well in React Router v3 due to doing all of the route matching outside of the React hierarchy, and not declaratively inside of it.
This was actually a user error. I expected getDataFromTree() to return a Promise that resolved to the original component chain (with the correctly injected GraphQL data props), but it actually just waits for the data to be ready.
The correct format is:
const Components = (
<StaticRouter location={ctx.request.url} context={route}>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</StaticRouter>
);
await getDataFromTree(Components);
const html = ReactDOMServer.renderToString(Components);

Multiple child routes in React-router

I am trying to implement a router where a Route has two parallel/independent sub-routes and I wonder if this is possible with react-router. The image below illustrates what I want to achieve. It represents an event-editor with two components: (1) a list of events (2) an editor for one event. I think their states should both be part of the URL somehow.
One extra constraint: [child1] should be aware of the state of [child2], since it needs to show the item that is currently edited in a different color or so.
If this is not possible with react-router, how would you implement this? Thanks!
Look this. By doing this you can create a parallel control of routes, without harming root.
/**at index.html*/
< div id="root"></div>
< div id="helper"></div>
/**at index.js*/
ReactDOM.render(
(
<BrowserRouter>
<App/>
</BrowserRouter>
),
document.getElementById('root')
);
ReactDOM.render(
(
<BrowserRouter>
<Helper />
</BrowserRouter>
),
document.getElementById('helper')
);
/**at Component Helper*/
render() {
const location = { pathname: '/2' }
return (<Switch location={location}>
<Route exact path='/2' component={Welcome} />
</Switch>);
}
const Welcome = () => <h3>welcome helper</h3>
//I'm using router V4 to switch of the routers.

Categories

Resources