Is there any way share state between - reactjs

I have four components: Panel, PanelHeader, PanelBody, PanelFooter they are composed as following:
<Panel>
<PanelHeader>
some title
</PanelHeader>
<PanelBody>
<div>
main content
</div>
</PanelBody>
<PanelFooter>
footer content
</PanelFooter>
</Panel>
I also have multiple routes, where I want to see that Panel, but with different content.
An obvious solution is to create components for each of routes and each of those components will have their own Panel.
But in my case I want to add some morphing animation between routes (change the height of panel, slide body, fade footer etc.). To achieve this I need to render all the contents to exactly the same Panel, PanelHeader, PanelBody, PanelFooter instances, so the solution above won't work.
So I need to split my route component into three components for each PanelXXX and the problem is that those components need to react on events of each other.
There are the following points so far:
I can't create another one mediator component to control those three partial components, because I will not be able to access its API till it will be rendered (so I can't implement getter methods e.g. getHeader()).
I can connect them all to redux, but it seams too complicated.
Another one solution is to create non-React object to share the state, but in this case I will need to clean up after this object to overcome memory leaks. The connection with redux will also be tricky. This approach looks not so clean to me.
I can also cheat with transparent background, so that I can have one Panel but multiple PanelHeaders etc. But in this case I will need to spread the knowledge about animation to all the child components.
Does anybody knows any other approach to do that in React? Or should I choose one from the points above?

I think that the scenario you are describing and the questions you pose are the exact rationalizations for using Flux. Redux is one option. We used a plain vanilla Flux implementation using the original Facebook library and we have had great success. It has a steep learning curve, but once you get your head around it, issues like the one you are facing just fit right in to the infrastructure.

Sounds like you could benefit from using this.props.children.
Router
<Router history={createBrowserHistory()}>
<Route path="/" component={App}>
<IndexRoute component={DefaultView} />
<Route path="foopath" component={FooView} />
<Route path="barpath" component={BarView} />
</Route>
</Router>
App.js
...
render: function() {
return (
<Panel>
{this.props.children}
</Panel>
);
}
...
DefaultView.js
render: function() {
<div>
<PanelHeader>
FooHeader
</PanelHeader>
<PanelBody>
FooContent
</PanelBody>
<PanelFooter>
FooFooter
</PanelFooter>
</div>
}
You can read more about React's children here.

Related

Nested React Router won't render

I have a popup component overlaying the main content that I want to render through routing, but because I want it to build on existing routes without messing up my main page configuration, I have tried going for a nested route. However, I seem to have misunderstood how it is supposed to be done. I wrapped the export with "withRouter" and tried to do this:
<Fragment>
<Helmet>
<title>Videos - Saddex Productions</title>
</Helmet>
<Switch>
<Route path="/popup"
render={() => <Popup items={props.videos}/>}
/>
</Switch>
...
This doesn't work and only renders the main component. And I also don't want to outsource the code that's going to be rendered underneath, because it seems unneccessary. However, what should I do? Thanks in advance.
Solved: The reason it didn't work is because I presumed the router works like the Express router does - with relative paths. I included the base path like "videos/popup" and now it works, it seems.

Update react Context from children or alter component structure?

This question focuses on the Header component: So in my app. I have a something that looks a bit like this with a few more components and plenty more routes:
return (
<SiteLayoutContext.Provider value={siteConfiguration} >
<div className="topContainer">
<BrowserRouter>
<div className="header">
<Header/>
</div>
<div className="mainWrapperContainer">
<div className="contentWrapper">
<Route exact path='/login' component={Login}/>
<Route exact path='/home' component={Home}/>
<Route exact path='/about' component={About}/>
<Route exact path='/collection/:collectionName' component={CollectionLandingPage}/>
<Route exact path='collection/:collectionAlias/id/:itemId' component={ItemView}/>
</div>
<Footer/>
</div>
</BrowserRouter>
</div>
</SiteLayoutContext.Provider>
)
What I'd like to accomplish:
I can currently set a logo for the header. This is great, but I'd like the user to be able to select a custom logo, one that will replace the global default, if they are within a collection.
To define 'within' a collection, the route will start with /collection/ so in the example below, both the ItemView and the CollectionLandingPage should have this custom logo.
I need to be able to tell my header that it's currently within a collection, and I want to limit renders and redraws to improve performance. I have an api endpoint that I fetch to grab collection info, which would tell me if a custom logo has been set, and what the href should be.
What I've tried: My original thought was to rip the header out of the main return() shown above, and instead place it within every component, that way I can use React's useLocation() hook to check the the url, and only fetch the collection's logo if1) we're within a collection and 2) a custom logo has been set.
My issue with this method is that I now have to enter the Header within every single component, instead of how it is currently implemented.
My second thought is to wrap it all within some kind of context, consume it in every component, and update it based on the url, which would then fetch the relevant info, but this just feels off. I suppose I could fetch the information for the collection and update a new CollectionInfoContext every time I hit /collection/:collectionName which would only update when the collectionName changes?
I'm kind of just looking for suggestions on how to implement this because well... I feel like there's an option I haven't listed that's probably much easier and more reasonable... as is the stackoverflow standard.
Disclaimer: I'm a react context api novice
If you're not using Redux or another state management solution, a context is definitely a reasonable way to share data across your application, UI themes are actually one of the recommended use cases in the React docs.
The only issue I could see is that changing the state for only certain components would require a way of resetting the context for other places where you just want a standard logo. But reading the URL name could solve that by simply storing the path name and using it inside of Header to render the custom logo if the path includes collections. If Header can read the path name all on its own, you may not even need a context if that's the only condition that's dynamic everywhere.

Is this a good alternative to react-redux?

I don't want to use redux because there's too much boilerplate. So I am looking for a way to share a global state between components. Is it ok to pass the whole state of the App (root) component?
I have no issue with it so far but there must be a hole down the road, is there?
<HashRouter>
<App />
</HashRouter>
<Switch>
<Route exact path='/' render={props => <TheListForm appState={this.state} app={this} />} />
<Route exact path='/myvideos' render={props => <MyVideos appState={this.state} app={this} />} />
</Switch>
So in my child components I have "this.props.appState" which contains the equivalent of the redux store and I can update it simply by doing:
this.props.app.setState({name:"bob"});
If you want a smaller, with less boilerplate and with a lower learning curve state management library, you could check out mobx. You want to use a state management library, if you component exceeds more than a few child component, because your app will rerender everything on every change. With this.props.appData.setState and a form your while app would rerender if the user types something. It also breaks the react one way data flow concept.
Imagine that you build a large scale project and there are so many hierarchies.
A includes B, B to C, ..., Y to Z.
If you want to pass the props from A to Z, you must set properties of all components from B-Z.
In that case, Redux is very useful and you only correct 2 components A and Z.
And also you can implement the bi-directional data flow with Redux.
https://rootzjs.org/ you should give it a try with this one. dead easy to use.

Real-Time use case of this.props.children

I have read many articles to find out the real time use case of this.props.children but i didn't find the answer that i am looking for.I know that this.props.children is used to access the data b/w the opening and closing tag of a component. But my question is why can't we add a prop to the component instead of writing data b/w opening and closing tag.
for Ex:
<Example>This is data<Example> //can be accessed as this.props.children
can be written as
<Example data="This is data"/> //can be accessed as this.props.data
Can somebody please explain me with a real-time example of where we can achieve a certain task by using only this.props.children?
For example if you have complicated children of a component:
<Card>
<div class='title'>Title</div>
<div class='content'>Content</div>
</Card>
It would be easier than if you write like:
<Card content={[<div class='title'>Title</div>, <....>]} />
Samething you can find here, for example in Drawer component of Material-UI here. Drawer is a component that slides from the left, it can contain anything, so using props.childrens.
While making an app, you want a parent component which will render anything in your component. The use cases which I can think of are:
When you want to open a different component depending upon the route change.
const App = ({ children }) => (
<div className="full-height">
{children}
</div>
);
When you want to have same styles throughout your app for generic elements such as body, head etc. You'll just have to apply on this component, e.g., in above example, the full-height will get applied everywhere in the app on top component. (Obviously there are other work arounds but this is always more clear)
For use cases where you want to expose your component (when component doesn't know children ahead of time) as libraries and props can vary a lot and complicates the rendering. Read this
Obviously you don't have to use it but it makes code more elegant and understandable.

react-router | how to for render of Router if components state changes

I have simple main.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import App from "./components/app";
ReactDOM.render(<App />,
document.getElementById('appContainer')
);
The App component defines routes:
render(){
console.log('app', this.state.current, this.state.global);
return (
<Router>
<Route path="/" component={Template} current={this.state.current}>
<IndexRoute component={GlobalList} global={this.state.global} />
<Route path="current" component={CurrentList} current={this.state.current} />
<Route path="item/:item" component={ItemDetail} />
</Route>
</Router>
);
}
I use the re-base package to communicate with my Firebase database. This library doesn't allow to do syncState for one endpoint more than once for all application. This is a reason, why I get those data in App component and want to pass them to Routes. And this is a problem.
This console.log in render method is called 4 times. First two give empty data, third gives first endpoint and forth second endpoint. But Routes are not updated. I can see in Template element, that render method is called also 4 times, but every time props.route.current is an empty array, even if in App this.state.current is array with length > 0.
I would appreciate any hints how to solve this problem. if there is any other way to make it, I would love to read any good practices hints. I use newest versions of libraries, ES6 and webpack/babel to build.
Sometimes it is good to read all library documentation.
Thanks for #TylerMcGinnis for his patience and not using hard words :)
The solution for this problem is using bindToState method, if you want only bind changes to state, but not to post changes to Firebase. It was my case - only one component needs to sync changes, second one needs only listen to changes made by first one.

Resources