Is there a way to pass state to pages in Gatsby? - reactjs

Since Gatsby effectively hides the router, you can't pass props to each page in the way you would with BrowserRouter in Create-React-App. Is there a way to do this in Gatsby? I assume I need to do it somehow in Gatsby-browser.js. I basically want to maintain a state called Step, that is accessible by all pages. Would I have to use Context for this?

You can pass state into a page with Link (docs), but be mindful of pages being built without state (i.e. statically).
<Link
to={`/photos/`}
state={{ tag }}
>
See more {tag}
</Link>
const PhotosPage = ({ location: { state } }) =>
<div>{state?.tag ? `${tag} photos` : "All photos"}</div>

The native (and easiest) way is using React context but you have multiple ways of sharing a global state in Gatsby:
Using global wrappers within the wrapRootElement exposed API:
import React from "react"
import { ThemeProvider } from "./src/context/ThemeContext"
export const wrapRootElement = ({ element }) => (
<ThemeProvider>{element}</ThemeProvider>
)
If you follow this approach, you'll need to use wrapRootElement in both gatsby-browser.js as well gatsby-ssr.js since it's a shared API.
Keep in mind that Gatsby's router extends from #reach/router (from React).
In that way, you'll keep your step state across the application.

Related

Navigation links server side rendering in nextjs

i'm still new to nextjs and i'm building static site, i'm trying to make server responsible for rendering navigation links but i can't figure this yet how to do this and how to fetch those links
as you know navigation links lies in seperate component and not included in every page and also fetching data must be in nextPage not in the components
the only solution i have is to fetch navigation links when home page loads and then get the navigation links as props and pass it the context but this will make the client responsible for rendering the naviagtion
my _app.js is
import '../styles/globals.scss'
import { Layout } from '../components'
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default MyApp
as you see the Layout contains the navigation and Component has the rest
The global data should come from the layout, not from the page. If you have more than one page, you don't want to repeat your API calls in each pages.
If you use the nextjs 13 (still in beta atm), you can do this in the app/ directory. If you don't, you will need to use useSwr in your layout : https://nextjs.org/docs/basic-features/layouts.
To summarize, if you really want to be client agnostic on this feature, i think you need to try nextjs 13.

How do I change the styles of some shared component in React and I have configured routes with react route

I am working on a web app that is consisted of few pages and have configured routes with react-route-dom. On the pages, I have some shared components and I want them to be styled differently on other pages except the Home page.
How do I get this done? Any help?
Thanks
Check if pathname is path of home or else not you can use useLocation to get the pathname and add conditional styling or classes.
useLocation v5
useLocation v6
import React from 'react';
import {useLocation} from 'react-router-dom';
export const Common_Component=()=>{
const location=useLocation()
const pathname=location.pathname
return(
<div>
<div classes={pathname=="home"?"home_class":"other_class"}>
...
</div>
</div>
)
}
Or
You can pass boolean props to the component to let component know it should use home bases classes or other classes.
PS.
you will still need to do conditional check if props is true or false
useMatch you can use useMatch to match the current url with regex.
While it is always good to share code, I would recommend creating a new component for the homepage. Otherwise you are coupling your components. What happens if you want to add another specialisation for the whatever-component? You add another case! Your components grow and wire up more and more until you have one big pot of spaghetti. Hence, it makes sense in many situations to just create a new component for a new use-case.

react redux: private route not rendering layout

Code Sandbox link:
and trying to follow this article
On successful login(/auth/login), the user should be routed to the dashboard(/admin/summary). If the login is successful, I am also storing an access token.
I have a PrivateRoute component for this. The problem is that on successful login, the URL is getting updated but the component is not getting rendered.
PS: about the dashboard, this is a single page application so, the dashboard has topbar, sidebar, and the right content and altogether these things are coupled inside <AdminLayout/>. So, in my AppRouter, I have to render the <AdminLayout/> and just any one component.
All the react and redux code is included in the code sandbox.
Since in your code you create your own history object (it happens in you history.js file, when you call createBrowserHistory()) but doesn't pass it to your Router, nothing happens.
There are 2 possible solutions:
1. Don't create a history object yourself, but use useHistory hook inside your component
Working Demo
With this approach, you should remove history.push from login.actions.js (which imports history) and use history.push in Login.js (which uses useHistory hook):
// login.actions.js
...
loginService.login(userid, password, rememberPassword).then(
(userid) => {
dispatch(success(userid, password, rememberPassword));
// history.push(from); <-- commented out!
},
(error) => { ... }
);
};
...
// Login.js
function handleSubmit(e) {
...
const { from } = {
from: { pathname: "/admin/summary" }
};
history.push(from) // <-- added!
dispatch(loginActions.login(inputs, from));
...
}
useHistory exposes the history object of BrowserRouter (I think this is implied in this official blog post).
2. Create a history object yourself, but pass it to a Router component
Working Demo
This approach would require you to make several changes:
Creating the history object on your own means you become responsible to provide it to a router component, but it can't be a BrowserRouter, but the base Router component (see these Github answers: 1, 2).
Once you import Router (instead of BrowserRouter), you need to get rid of any useLocation and useHistory imports, otherwise you'll get errors.
I also had to unify the history object export and imports, so that it is exported as the default export (i.e., export default history), and it is imported as the default import (i.e., import history from "./history"; instead of import { history } from "./history")
(P.S: this approach can be seen implemented elsewhere on SO, for example here or here (the latter explicitly installs history, but it's not needed in your case).

How to add a list of global components in nextjs

I'm new in Nextjs and I want to add a list of components and utilities to all of my pages in Nextjs. How can I make them all available in my components without importing them one by one.
Also some utilities might be needed in getInitialProps function in pages like axios or in my case client of Apollo.
I don't expect some one write me a whole config file for Nextjs.config just help me to understand the configuration of next deeper.
thanks in advance
I would suggest to do dynamic import on each page, for each component and utility instead of making components global. By doing so Next.js will create a small chunks or "pages" without any unused components.
If you load components globally, every page would increase in size but actually not use these components or utilities. It defeats a purpose of code-splitting.
It breaks modularity of your code, because components/pages (modules) will depend on some global utilities implicitly.
If you have too many imports in your component or page, probably, it's a sign to encapsulate that extra functionality by extracting it to it's own component.
Controversially, you could try to add global utilities in Custom "App"
For example, you could pass a React component to all Next.js pages via props of custom App.
_app.js
import Icon from '../components/Icon';
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} icon={Icon} />
}
As all pages are instancing from the App, every page would have this component in the props.
index.js
const Home = props => (
<div>
<props.icon><props.icon/>
</div>
);
If you have many components you could group them into list and assign to a single prop, so you can use it like:
index.js
const Home = props => (
<div>
<props.components.icon/>
<props.components.anotherIcon/>
</div>
);
On a side note, it's suggested to use getStaticProps and getServerSideProps for pages instead of getInitialProps since Next.js 9.3.
Suggested reading:
Next.js Data fetching (getInitialProps)
React.js Code-Splitting

How does the react `Router` component communicate with the `Route` component?

I know that the Router component, say BrowserRouter creates a history object for keeping track of paths and so on, and that a Route component renders its view only when its path matches the current location, which I assume is read from the history. My question is how does the Route component get access to the history object that is created by the BrowserRouter. Is there some under the hood communication going on that is making this possible?
This is a context api 'magic'.
<BrowserRouter/> renders <Router/>:
class BrowserRouter extends React.Component {
history = createHistory(this.props);
render() {
return <Router history={this.history} children={this.props.children} />;
}
}
<Router/> renders <RouterContext.Provider/>:
<RouterContext.Provider
children={this.props.children || null}
value={{
history: this.props.history,
<Route/> uses <RouterContext.Consumer> to provide access to data from provider.
Using context api allows to provide data/methods many levels down the tree without the need of passing down props expliticely (on each level). You can read more about this on docs or search for tutorials.

Resources