React mfe: Communication between mfe's - reactjs

With react 17, I am trying with a mfe demo. There is a parent container that contains two mfe's, Header and Auth.
When user logs in, the container is notified. Upon receiving this notification, the header component needs to be notified about this event and allows to update itself.
Currently the container could get the message from Auth-mfe but got struck with how the Header component to subscribe to this with container.
The goal is to minimize or eliminate direct communication between mfe's and if any needed, it has to go through the parent container.
I am not using redux or any third party library for storing the state. The user session details are currently stored in sessionStorage by Auth-mfe.
Container app.js snippet is
function App() {
return (
<BrowserRouter>
<div>
<Header notifySignedIn={notifySignedIn} />
<Suspense fallback={<Progress />}>
<Switch>
<Route path="/auth" render={props => (<AuthLazy onSignin={onSignin} {...props} /> )} />
</Switch>
</Suspense>
<Footer />
</div>
</BrowserRouter>
);
}
export default App;

Referring to this post , I implemented passing the callback function from container => header and in turn header will pass the subscribe function back to the container. This way it works.

//add the code in componentdidmount where you want to receive the data
window.addEventListener("event-name", (datarecieved)=> {
console.log(datarecieved.detail);
}, false);
//Create an event to pass data
let send-data = new CustomEvent("event-name", {detail: "Hi Suraj here"});
window.dispatchEvent(send-data);

Related

React Router: navigate to /compose/tweet but keep orthogonal previous route (like: /notifications) mounted

I am seeking to recreate a pattern with React Router. It is best described by the Twitter example: as you hit the Tweet button, the browser navigates to /compose/tweet, mounting the composer component. However, and that's the key, the previous route (/home, /explore, /notifications, /messages) is kept mounted despite the route change. How do you do that?
This could be called bidimensional routing: the /compose/tweet route is kept orthogonal with respect to the other routes that render the main view. The other routes are hidden (i.e., not in the address bar any longer) upon navigating to /compose/tweet, thereby rendering two independent routes (say, /notifications and /compose/tweet) at once.
My actual example: I need to show a user settings menu (/user/menu) as a large sidebar, but I do not want that to change whether the user was navigating / (the homepage), /faq, /contact, etc. Based on my current understanding of React Router, as soon as you hit /user/menu, any other route (take /faq as an example) would be unmounted based on route match.
Caching the previous route (e.g., Redux, which I'm using extensively already) does not seem feasible, since, even though I would be able to redirect the user to the previous route upon exiting /user/menu, React would still be unmounting components, in fact showing the homepage in the background until the user exits /user/menu & gets redirected to where they were at, which is not the intended behaviour. I would want the rest of the page to stay there with the rendered components, just the way Twitter does.
Am I overlooking anything? Is this an easy pattern and I am missing something?
Note: it's a SSR isomorphic app, but I guess/hope that won't change things.
Despite Adam Abramov's suggestion to keep React Router as the source of truth for whatever can be passed as route, and avoid deep integrations between Router and Redux, I found myself having to use Redux as the main source of truth in this (important) use case. I still wanted to have Route components for SSR and SEO purposes.
So, I created my own MultidimensionalSwitch and MultidimensionalRoute components to solve this use case. If a MultidimensionalSwitch is mounted, it will render the components at their subroutes, but if none of them is matched, it will render them based on another dimension, which is provided by Redux at an additional alt property of the corresponding MultidimensionalRoute.
Here below is some code, feel free to answer/ask should you need more info about it.
Main
class _Main extends Component {
render() {
const {menuOpen,selected} = this.props;
return (
<Fragment>
<Route exact path={exactRoutes.ROOT} component={() => <Redirect to={selected?exactRoutes[selected]:exactRoutes.HOME} />} />
<Header />
<Route path={nestedRoutes.AUTH+routeParams.LOGIN_SIGNUP.key} component={ScreenAuth} />
<Route path={exactRoutes.USER_MENU} component={ScreenUser} />
{!menuOpen?"":<ScreenMenu />}
<ScreenMain>
<MultidimensionalSwitch>
<MultidimensionalRoute path={exactRoutes.HOME} alt={selected===guestMenuOption.HOME} component={GuestHome} />
<MultidimensionalRoute path={exactRoutes.USER} alt={selected===guestMenuOption.USER} component={User} />
<MultidimensionalRoute path={exactRoutes.VISION} alt={selected===guestMenuOption.VISION} component={GuestVision} />
<MultidimensionalRoute path={exactRoutes.FAQ} alt={selected===guestMenuOption.FAQ} component={GuestFaq} />
<MultidimensionalRoute path={exactRoutes.INFOGRAPHICS} alt={selected===guestMenuOption.INFOGRAPHICS} component={GuestInfographics} />
<MultidimensionalRoute path={exactRoutes.BLOG} alt={selected===guestMenuOption.BLOG} component={GuestBlog} />
<MultidimensionalRoute path={exactRoutes.CONTACT} alt={selected===guestMenuOption.CONTACT} component={GuestContact} />
</MultidimensionalSwitch>
<Footer />
</ScreenMain>
<Flare />
</Fragment>
);
};
}
const mapStateToProps = state => ({
menuOpen: state.client.guest.menuOpen,
selected: state.client.guest.guestMenuOption,
});
const Main = withRouter(connect(mapStateToProps,{})(_Main));
export default Main;
MultidimensionalSwitch
class _MultidimensionalSwitch extends Component {
render() {
return (
<Fragment>
<Switch>
{this.props.children}
{this.props.children.map(child => !child.props.alt?"":<Route path={nestedRoutes.ROOT} component={child.props.component} />)}
</Switch>
</Fragment>
);
}
}
const mapStateToProps = state => ({
selected: state.client.guest.guestMenuOption,
});
const MultidimensionalSwitch = withRouter(connect(mapStateToProps,{})(_MultidimensionalSwitch));
export default MultidimensionalSwitch;
MultidimensionalRoute
class _MultidimensionalRoute extends Component {
render() {
const {path,component} = this.props;
return (
<Fragment>
<Route exact path={path} component={component} />
</Fragment>
);
}
}
const mapStateToProps = (state,ownProps) => ({
path: ownProps.path,
component: ownProps.component,
});
const MultidimensionalRoute = withRouter(connect(mapStateToProps,{})(_MultidimensionalRoute));
export default MultidimensionalRoute;

How to Use Router with 2 different components?

I'am having issues trying to figure it out how to use Router with more then one Wrapper Component.
So my app has a Wrapper component that makes a get call validates the token and has a url parameter. This url is the link to fetch from my api.
I have a users.jsx file that lists the users and a userdetails.jsx file that lists the details of each user. The key is the CN.
My users.jsx already has a dynamic link that uses the username(cn) for each user in the list.
Now my app renders the user list in my users.js file using the Wapper component and passing the url to fetch for the user list.
What i need is to have the userDetails integrated here.
users.js
const IndexPage = () => (
<Layout>
<MainComponentWrapper url="http://localhost:5000/user">
<User />
</MainComponentWrapper>
</Layout>
)
My other url is: http://localhost:5000/user/<cn>to fetch for the details.
I was trying to come with a code like this, but it doesn' work:
const IndexPage = () => (
<Layout>
<Router>
<div>
<MainComponentWrapper url="http://localhost:5000/user">
<User path="/users" />
</MainComponentWrapper>
</div>
<div>
<MainComponentWrapper url="http://localhost:5000/user/<cn>">
<UserDetails path="/users/:cn" />
</MainComponentWrapper>
</div>
</Router>
</Layout>
)
I want to navigate to the user details if i click on a user in the list
Also i am using gatsby plugin :
{
resolve: `gatsby-plugin-create-client-paths`,
options: { prefixes: [`/user/*`] },
},
```
refer this react-router-exact , read the first answer , then read the second answer , you need to undestand the concept of exact (its described in answer 1) in react router then you have to combine it with the switch (in the answer two) statement .

How to expose context data to specific routes

I'm using react hooks with context API for sharing data between multiple components and using reach router for routing. My code looks something like this:
function App() {
return (
<div className="App">
<ContextProvider>
<Router>
<Comp1 path='/comp1' >
<Comp2 path="/"/>
</Comp1>
<Comp3 path="/comp3" />
</Router>
</ContextProvider>
</div>
);
}
In this example, whatever data exposed by ContextProvider will be available to all the components. My question is, how can I expose the context only to Comp3 but not to Comp1 and Comp2?
Note: I'm using reach router.
#Tien Duong, we had the same thought as yours. But it turns out the reach router internally works by mapping the component to the location configured to render them, it does not even take div
Funny how client side routing it taking shape, also they say they would be passing some route props to the Component mapped with the location.
It's like opening your house for anyone to enter. Where is the isolation in this case.

Ideal way to load components in sequential order in React.js

I have a question regarding component loading in React.js.
Is there a way to load components in a specific order. For instance I have the components for each page of my single page website.
I would like <homepage /> and it's images to load first than <profile />, then <works />, and so on...
The idea being that when a user first visit my site, homepage will load instantly(or really quickly) and the rest can follow so at least the user has something to look at before they potentially start scrolling.
I've tried to google "loading components in order with react.js" but no luck.
class MainContent extends React.Component {
componentDidMount() {
console.log("component mounted");
}
render() {
return(
<div className="main-content">
<ul className="block">
<HomePage />
<ProfilePage />
<WorksPage />
<ContactPage />
<BlogPage />
</ul>
</div>
)
}
}
My first idea would be to have a promise in <componentDidMount /> function which would switch a boolean state to true when <Homepage /> is loaded.
<Homepage />
{
this.state.loaded ?
<ProfilePage />
<WorksPage />
<ContactPage />
<BlogPage />
:
null
;
}
Am I on the right track with that train of thought?
Any help is much appreciated.
Thanks,
Moe
You are doing just fine. It is a recommended approach:
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here. If you
need to load data from a remote endpoint, this is a good place to
instantiate the network request. Setting state in this method will
trigger a re-rendering.
So initially in render method render only portions on UI that are immediately available. then in componentDidMount - fire and wait for completion of your promise to fetch the rest of the data, and afterwards call setState to refresh UI to show in full.

Set state property from URL using react-router

I have a container component with a modal in it that is opened and closed based on a state property.
I want to control this via the URL, i.e. I want to have
/projects - the modal is NOT open
/projects/add - the modal IS open
As well as being able to link directly to it, I want the URL to change when I click on links within the main container to open the modal.
Can someone explain how I could do this, or point me in the right direction of a good tutorial?
NOTE: This way is not perfect. Even more it's rather antipattern than pattern. The reason I publish it here is it works fine for me and I like the way I can add modals (I can add modal to any page and in general their components don't depends on the other app in any way. And I keep nice url's instead of ugly ?modal=login-form). But be ready to get problems before you find everything working. Think twice!
Let's consider you want following url's:
/users to show <Users /> component
/users/login to show <Users /> component and <Login /> modal over it
You want Login to not depend on Users in anyway, say adding login modal to other pages without pain.
Let's consider you have kinda root component which stands on top of other components. For example Users render may look something like this:
render() {
return (
<Layout>
<UsersList />
</Layout>
);
}
And Layout's render may look something like this:
render() {
return (
<div className="page-wrapper">
<Header />
<Content>
{this.props.children}
</Content>
<Footer />
</div>
);
}
The trick is to force modal's injection to <Layout /> every time we need it.
The most simple approach is to use flux for it. I'm using redux and have ui reducer for such page meta-information, you can create ui store if you use other flux implementation. Anyway, final goal is to render modal if <Layout />'s state (or even better props) contains modal equal to some string representing modal name. Something like:
render() {
return (
<div className="page-wrapper">
<Header />
<Content>
{this.props.children}
</Content>
{this.props.modal ?
<Modal key={this.props.modal} /> :
null
}
<Footer />
</div>
);
}
<Modal /> returns modal component depends on given key (In case of our login-form key we want to receive <Login /> component).
Okay, let's go to router. Consider following code snippet.
const modal = (key) => {
return class extends React.Component {
static displayName = "ModalWrapper";
componentWillMount() {
// this is redux code that adds modal to ui store. Replace it with your's flux
store.dispatch(uiActions.setModal(key));
}
componentWillUnmount() {
store.dispatch(uiActions.unsetModal());
}
render() {
return (
<div className="Next">{this.props.children}</div>
);
}
}
};
...
<Route path="users" component={Next}>
<IndexRoute component={Users}>
<Route path="login" component={modal('login-form')}>
<IndexRoute component={Users} />
</Route>
</Route>
(Don't care about Next - I add it here for simplicity. Imagine it just renders this.props.children)
modal() function returns react component that triggers change in ui store. So as soon as router gets /users/login it adds login-form to ui store, <Layout /> get it as prop (or state) and renders <Modal /> which renders corresponding for given key modal.
To programmatically assess to a new URL, pass the router to your component and use push. push for example will be call in the callback trigger by the user action.
When setting your router set a route to /projects/:status. then, in your component route, you can read the value of status using this.props.param.status. Read "whats-it-look-lik" from react-router for an example.

Resources