How to use getInitialProps in _app.js file with function component? - reactjs

I just see code _app.js file like this
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const { token } = parseCookies(ctx);
return { pageProps };
}
render() {
const { Component, pageProps } = this.props;
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
}
export default MyApp;
This is _app.js file
function App({ pageProps, Component }) {
return (
<Layout >
<Component {...pageProps} />
</Layout>
)
}
export default App;
I want to convert first code examle to this. How to do this?

You should be able to set it up as follows, but be aware when you have getInitialProps on your app you are blocking every page, see more details here: https://nextjs.org/docs/advanced-features/custom-app
function App({ pageProps, Component }) {
return (
<Layout >
<Component {...pageProps} />
</Layout>
)
}
App.getInitialProps = async (appContext) => {
// Logic goes here
}
export default App;

Related

How to define more layouts for subdirectories in NextJS?

I have the main layout that wraps whole app:
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Layout>
<Component {...pageProps} />
</Layout>
</>
);
}
I want to have the main layout and another layout to wrap, for example localhost:3000/subdirectory/... subdirectory.
You can do it simply like below:
First, remove the layout from your _app.js
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
</>
);
}
Second, create you first layout component:
const MainLayout = ({ children }) => {
return (
<>
<main>{children}</main>
</>
);
};
export default MainLayout;
Then Second layout:
const Dashboard = ({ children }) => {
return (
<>
<main>{children}</main>
</>
);
};
export default Dashboard ;
Then you can import your layout and use it like in your component:
import Dashboard from './Dashboard ';
export default function Card(){
return(
<Dashboard><div>your code</div><Dashboard>
)
}
And:
import MainLayoutfrom './MainLayout';
export default function Card(){
return(
<MainLayout><div>your code</div><MainLayout>
)
}

pass setState from parent to its child component using React Hook

How do I pass setState from parent to its child component using React Hooks? Code is provided:
App.js
export default App() {
<Main>
<Widget/>
</Main>
}
Main.js
export default Main() {
const [marketProducts, setMarketProducts] = useState([]);
...
return
<DataContext.Provider value={marketProducts}>
...
{props.children}
</DataContext.Provider>;
}
Widget.js
export default Widget() {
return <div>
<NavComponent/>
...
</div>
}
NavComponent.js
export default NavComponent() {
// need setMarketProducts here but not sure how to bring from Main.js to here
return <div>
...
</div>
}
You can pass the setMarketProducts to you DataContext the same way you passed the marketProducts. Export Datacontext form Main.js. Although I suggest to create seprate file for your context
export default Main(props) {
const [marketProducts, setMarketProducts] = useState([]);
...
return
<DataContext.Provider value={marketProducts, setMarketProducts}>
...
{props.children}
</DataContext.Provider>;
}
Then use useContext hook in NavComponent.js to access setMarketProducts
import { DataContext } from 'Main'
...
export default Widget() {
const { setMarketProducts } = React.useContext(DataContext)
return <div>
...
</div>
}
You can pass setMarketProducts down the chain as shown in example below. Other wise you can use some kind of store e.g. useContext or redux
export default App() {
const [marketProducts, setMarketProducts] = useState([]);
<Main setMarketProducts={setMarketProducts} marketProducts={marketProducts}>
<Widget setMarketProducts={setMarketProducts}/>
</Main>
}
export default Main({ setMarketProducts,marketProducts, ...props }) {
...
return
<DataContext.Provider value={marketProducts}>
...
{props.children}
</DataContext.Provider>;
}
export default Widget({ setMarketProducts }) {
return <div>
<NavComponent setMarketProducts={setMarketProducts}/>
...
</div>
}
export default NavComponent({ setMarketProducts }) {
// need setMarketProducts here but not sure how to bring from Main.js to here
return <div>
...
</div>
}

Hiding Banner at a certain page

I'm currently attempting to hide the banner at a certain page. I have successfully hid the banner in all other pages except one with a page with a id. I have a dynamic folder named [content]
import { useRouter } from "next/router";
const HIDDEN_BOARDLIST = ["/board/board_list"];
//this is successful
const HIDDEN_BOARDDETAILS = [`board/${content}`].
//this does not work
//http://localhost:3000/board/620471f057aad9002de7f04f. I have to enter the id manually but since this is a dynamic, the id will change every time
export default function Layout(props: ILayoutProps) {
const router = useRouter();
console.log(router.asPath);
const isHiddenBoardList = HIDDEN_BOARDLIST.includes(router.asPath);
return (
<Wrapper>
<Header />
{!isHiddenBoardList && <Banner />}
<BodyWrapper>
<Body>{props.children}</Body>
</BodyWrapper>
</Wrapper>
);
}
useRouter is a hook.
CSR
import React, { useState, useEffect } from 'React';
import { useRouter } from "next/router";
interface ILayoutProps {
//...
}
export default function Layout(props: ILayoutProps) {
const router = useRouter();
const [hidden, setHidden] = useState(false);
useEffect(() => {
if(router.asPath.includes('board/')) {
setHidden(true);
}
}, [router.asPath]);
return (
<Wrapper>
<Header />
{!hidden && <Banner />}
<BodyWrapper>
<Body>{props.children}</Body>
</BodyWrapper>
</Wrapper>
);
}
Since this code is CSR, flickering may occur. <Banner /> will disappear after being rendered.
If you don't want that, there is a way to pass the current url as props of the <Layout /> component via getServerSideProps.
SSR
// pages/board/[id].tsx
import { GetServerSideProps, NextPage } from 'next';
import Head from 'next/head';
interface Props {
url: string;
}
const BoardPage: NextPage<Props> = (props: Props) => {
return (
<>
<Layout {...props} />
</>
);
};
export const getServerSideProps: GetServerSideProps = async (context) => {
const { resolvedUrl } = context; //ex) /board/12345?id=12345
return {
props: {
url: resolvedUrl ,
}, // will be passed to the page component as props
};
};
// components/Layout.tsx
import React, { useState, useEffect } from 'React';
import { useRouter } from "next/router";
interface ILayoutProps {
url: string;
// ...
}
export default function Layout(props: ILayoutProps) {
return (
<Wrapper>
<Header />
{props.url.includes('board/') && <Banner />}
<BodyWrapper>
<Body>{props.children}</Body>
</BodyWrapper>
</Wrapper>
);
}
I hope these two kinds of code are helpful.

Cannot get redux state inside the provider file

I'm trying to get the redux state value in the same file as where I use the provider.
For some reason it seems it cannot find the value.
const MyApp = ({ Component, pageProps }: AppProps)=> {
const isDark = useSelector<ThemeState, ThemeState["isDark"]>(state => state.isDark)
const dispatch = useDispatch()
return (
<>
<Provider store={ThemeStore}>
<div className={isDark ? 'dark' : 'white'}>
<Player />
<Component {...pageProps} />
</div>
</Provider>
</>
)
}
export default MyApp
This gives an error:
Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
When using the same useSelector and dispatch inside my nav component, it does work.
Any idea how I could make it work inside the _app.js file?
You can not use state in the provider like that, you need to go at least one layer deeper or just use what you're passing to the state directly not from calling to useSelector, try this:
function Child({ children }) {
const isDark = useSelector<ThemeState, ThemeState["isDark"]>(state => state.isDark)
return <div className={isDark ? "dark" : "white"}>{children}</div>;
}
const MyApp = ({ Component, pageProps }: AppProps) => {
return (
<Provider store={ThemeStore}>
<Child>
<Player />
<Component {...pageProps} />
</Child>
</Provider>
);
};
export default MyApp;

NextJS - Passing state to children as props in custom app

I have this ./pages/_app.js from the official docs:
import App, { Container } from 'next/app'
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
render () {
const { Component, pageProps } = this.props
return (
<Container>
<Component {...pageProps} />
</Container>
)
}
}
export default MyApp
I want to pass state from MyApp to every page it renders, but can't figure how. I tried this:
const childProps = {
...
}
<Container>
<Component {...childProps} />
</Container>
and got an error:
React.Children.only expected to receive a single React element child.
pass what you want just as you pass usual props
<Component whatyouwant={propsyouwant} />
For example: this is how I passed MobileDetect props to every component in my app:
class MyApp extends App {
static async getInitialProps({ Component, router, ctx }) {
let pageProps = {}
const { req } = ctx
var MobileDetect = require('mobile-detect')
const md = new MobileDetect(req ? req.headers['user-agent'] : "")
const phone = md.phone()
const tablet = md.tablet()
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps,
phone: phone,
tablet: tablet }
}
render () {
const {Component, pageProps, phone, tablet} = this.props
return (
<Container>
<Provider store={reduxStore}>
<Nav />
<Component {...pageProps} phone={phone} tablet={tablet} />
<Footer />
</Provider>
</Container>
)
}
}
export default withReduxStore(MyApp)
The culprit was
<Link>
<Icon />
text
</Link>
in the rendered page (not _app.js).
The Link had two children (remnants of react-router Link) instead of one.

Resources