Hiding Banner at a certain page - reactjs

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.

Related

props return undefined from NextJs

I'm trying load an API response into a Next.js page but the props return undefined. I went through lot of answers from StackOverflow still couldn't find a solution to my problem.
Edited this post added a few more details for support.
Here is my code below.
_app.tsx
import 'tailwindcss/tailwind.css';
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { UserProvider } from '#auth0/nextjs-auth0';
import App from 'next/app';
function MyApp({ Component, pageProps }: AppProps) {
return (
<UserProvider>
<Component {...pageProps} />
</UserProvider>
)
}
MyApp.getInitialProps = async (appContext) => {
const appProps = await App.getInitialProps(appContext);
return {...appProps};
}
export default MyApp
profile.tsx
import React from 'react';
import { useUser } from '#auth0/nextjs-auth0';
import withAuth from '../components/withAuth';
import { Navbar } from '../components/navbar'
import styles from '../styles/Home.module.css'
import type { GetStaticProps, NextPage } from 'next';
export const getStaticProps: GetStaticProps = async(context) =>{
// const res = await fetch(`http://localhost:3000/api/users`);
const data = "sed"
console.log(data);
return {
props: {
users:data
}
}
}
const Profile: NextPage<{users:any}> = ({users}) => {
const {user, isLoading} = useUser();
console.log(users);
return (
<>
<Navbar />
<div className={styles.container}>
<div>profile</div>
</div>
</>
)
}
export default withAuth(Profile);
Here is the code for HOC that is wrapped for each page after login.
withAuth.tsx
import Login from "../pages/login";
import { Spinner } from "./spinner";
const withAuth = Component => {
const Auth = (props) => {
const { isLoggedIn, loading } = props;
if(loading){
return (
<Spinner />
)
}
if (!isLoggedIn) {
return (
<Login />
);
}
// If user is logged in, return original component
return (
<Component {...props} />
);
};
if (Component.getInitialProps) {
Auth.getInitialProps = Component.getInitialProps;
}
return Auth;
};
export default withAuth;

How can I pass setState function between contextProvider?

I'm having some problems to set a state when repass the context provider for other elements, this is my code.
I'm creating a FancyboxContext for when i need it, i can call it anywhere.
import React, { createContext, useContext, useState } from 'react';
interface FancyboxContextInterface {
fancybox: boolean;
setFancybox(value: boolean): void;
}
interface FancyboxProviderProps {
children: React.ReactNode;
}
interface UseFancyboxInterface {
fancybox: boolean;
setFancybox: React.Dispatch<React.SetStateAction<boolean>>;
}
const FancyboxInitialState = {
fancybox: false,
setFancybox: () => {}
};
const FancyboxContext = createContext<FancyboxContextInterface>(
FancyboxInitialState
);
const FancyboxProvider: React.FC<FancyboxProviderProps> = ({
children
}: FancyboxProviderProps) => {
const [fancybox, setFancybox] = useState(FancyboxInitialState.fancybox);
return (
<FancyboxContext.Provider
value={{
fancybox,
setFancybox
}}
>
{children}
</FancyboxContext.Provider>
);
};
export const useFancybox = (): UseFancyboxInterface => {
const context = useContext(FancyboxContext);
const { fancybox, setFancybox } = context;
return {
fancybox,
setFancybox
};
};
export default FancyboxProvider;
involving _document.tsx in ContextProvider;
...
render(): JSX.Element {
return (
<Html lang="pt">
<Head>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Roboto:wght#300;400;700&display=swap"
/>
</Head>
<body>
<FancyboxProvider>
<Main />
</FancyboxProvider>
<NextScript />
</body>
</Html>
);
}
but when I am trying to start a simple call to setState(), nothing happens
import React, { useEffect } from 'react';
import { AppProps } from 'next/app';
import { useFancybox } from '../hooks/useFancybox';
import { Fancybox, GlobalStyles } from '../styles/global';
const myApp: React.FC<AppProps> = ({ Component, pageProps }) => {
const { fancybox, setFancybox } = useFancybox();
useEffect(() => setFancybox(true), []);
return (
<>
{fancybox ? <Fancybox /> : ''}
<Component {...pageProps} />
<GlobalStyles />
</>
);
};
export default myApp;
First, you should know that React Context can only be used on the client side.
You cannot place your ContextProvider in _document.js because it only runs on the server side. in your _app.js you are exporting a javascript function myApp instead of a React component MyApp.
What you can do is place your FancyProvider in _app.js and call your setState in any page
what your _app.js should looks like
import React from "react";
import { AppProps } from "next/app";
import { GlobalStyles } from "../styles/global";
const MyApp: React.FC<AppProps> = ({ Component, pageProps }) => {
return (
<FancyboxProvider>
<Component {...pageProps} />
<GlobalStyles />
</FancyboxProvider>
);
};
export default MyApp;
Now you can call your setState on /pages/index.js or any other page
import { useEffect } from "react";
import { useFancybox } from "../hooks/useFancybox";
import { Fancybox } from "../styles/global";
export default function Home() {
const { fancybox, setFancybox } = useFancybox();
useEffect(() => {
setFancybox(true);
}, []);
return (
<div>
{fancybox ? <Fancybox /> : ""}
</div>
);
}

useContext return getState undefined in component children

I have a problem accessing the context within a child component, which is returning me undefined
router.js
class Home extends Component {
render() {
let { url } = this.props.match
return (
<div className='container-main'>
<Menu baseUrl={url} />
<Switch>
<Route exact path={`${url}/tasksVision`} component={Home} />
</Switch>
</div>
)
}
}
export default Home
index.js
import React from 'react';
import { PedidosProvider } from '../../../context/components/pedidos-pedidosVolume';
import Modal from './modal';
const Pedido = (props) => (
<PedidosProvider {...props}>
<Modal />
</PedidosProvider>
)
export default Pedido;
modal.js
this const "test" does works when called on that component
const Modal = () => {
const {
test
} = useContext(PedidosContext)
return (
<div>
{test} //this is working
<Orders/>
</div>
)
}
orders.js
this const "test" does not work when called on that component
import React, { useContext } from 'react'
import { PedidosContext } from '../../../context/components/pedidos-pedidosVolume'
const Teste = () => {
const { test } = useContext(PedidosContext) // this return: Cannot read property 'test' of undefined
return (
<h1>{test}</h1>
)
}
export default Teste
I haven't worked with the context API a lot but I think you are getting an error because you are destructuring the PedidosContext.
const { test } = useContext(PedidosContext)
should be
const test = useContext(PedidosContext)
Altough it can be that my answer is not what you are looking for. However, in the docs it is used the same way as I just described.

React: Cannot update a component from inside the function body of a different component

i'm trying to only render the component <IntercomClient /> after a user clicks "Accept" on a cookie consent banner. Clicking accept changes the GlobalLayout's intercomIsActive state to true and thereby renders the IntercomClient. This is working but the warning concerns me.
How can I workaround the child/parent state change? I've been looking around but don't really understand.
import React, { useState } from 'react'
import { CookieBanner } from '#palmabit/react-cookie-law'
import IntercomClient from '../components/intercomClient'
const GlobalLayout = ({ location, children }) => {
const [intercomIsActive, setIntercomIsActive] = useState(false)
return (
...
<CookieBanner
onAccept={() => setIntercomIsActive(true)}
/>
<IntercomClient active={intercomIsActive}/>
...
)}
IntercomClient
import React from 'react';
import Intercom from 'react-intercom'
const IntercomClient = ({ active }) => {
return active ? <div><Intercom appID="XXXXXX" /></div> : null
}
export default IntercomClient;
import React, {useState} from 'react';
const Example = () => {
const [intercomIsActive, setIntercomIsActive] = useState(false)
return (
<Layout>
...
<CookieBanner
onAccept={() => setIntercomIsActive(true)}
/>
<IntercomClient active={intercomIsActive}/>
...
</Layout>
);
};
export default Example;
import React, {useState} from 'react';
const Example = () => {
const [intercomIsActive, setIntercomIsActive] = useState(false)
return (
<Layout>
...
<CookieBanner
onAccept={() => setIntercomIsActive(true)}
/>
{
intercomIsActive &&
<IntercomClient active={intercomIsActive}/>
}
...
</Layout>
);
};
export default Example;

React complains element type is invalid when trying to use context

I'm trying to use React Context to update navbar title dynamically from other child components. I created NavbarContext.js as follows. I have wrapped AdminLayout with NavContext.Provider and use useContext in Course.js to dynamically update navbar title inside useEffect. However, when I'm doing this, react throws the following error on the screen.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
How can I use context properly so that I can update Header title from Course.js inside its useEffect?
NavbarContext.js
import React, {useState} from 'react'
export default () => {
const [name,setName] = useState("")
const NavContext = React.createContext({
name: "",
changeName: name => setName(name)
})
const NavProvider = NavContext.Provider
const NavConsumer = NavContext.Consumer
return NavContext
}
AdminLayout.js
<NavContext.Provider>
<div className={classes.wrapper}>
<Sidebar
routes={routes}
logoText={"Widubima"}
logo={logo}
image={image}
handleDrawerToggle={handleDrawerToggle}
open={mobileOpen}
color={color}
{...rest}
/>
<div className={classes.mainPanel} ref={mainPanel}>
<Navbar
routes={routes}
handleDrawerToggle={handleDrawerToggle}
{...rest}
/>
{/* On the /maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */}
{getRoute() ? (
<div className={classes.content}>
<div className={classes.container}>{switchRoutes}</div>
</div>
) : (
<div className={classes.map}>{switchRoutes}</div>
)}
</div>
</div>
</NavContext.Provider>
Navbar.js
import NavContext from "context/NavbarContext"
export default function Header(props) {
function makeBrand() {
var name;
props.routes.map(prop => {
if (window.location.href.indexOf(prop.layout + prop.path) !== -1) {
name = prop.name;
document.title = name;
}
return null;
});
return name;
}
return (
<AppBar className={classes.appBar + appBarClasses}>
<Toolbar className={classes.container}>
<div className={classes.flex}>
{/* Here we create navbar brand, based on route name */}
<NavContext.Consumer>
{({ name, setName }) => (
<Button
color="transparent"
href="#"
className={classes.title}
style={{ fontSize: "1.5em", marginLeft: "-2%" }}
>
{makeBrand() || name}
</Button>
)}
</NavContext.Consumer>
</Toolbar>
</AppBar>
);
}
Course.js
import React, { useState, useEffect, useContext } from "react";
import NavContext from "context/NavbarContext"
const AdminCourse = props => {
const context = useContext(NavContext);
useEffect(() => {
Axios.get('/courses/'+props.match.params.courseId).then(
res => {
context.changeName("hello")
}
).catch(err => {
console.log(err)
})
return () => {
setCourseId("");
};
});
return (
<GridContainer>
</GridContainer>
);
};
export default AdminCourse;
i think problem is there with your NavbarContext.js.
you are not exporting NavContext also.
you are defining provider, consumer but you are not using them either.
here's how you can solve your problem.
first create context and it's provider in a file as following.
NavContext.js
import React, { useState } from "react";
const NavContext = React.createContext();
const NavProvider = props => {
const [name, setName] = useState("");
let hookObject = {
name: name,
changeName: setName
};
return (
<NavContext.Provider value={hookObject}>
{props.children}
</NavContext.Provider>
);
};
export { NavProvider, NavContext };
in above code first i am creating context with empty value.
the i am creating NavProvider which actually contains value name as a state hook inside it.hookObject exposes state as per your naming conventions in code.
now i for testing purpose i defined two consumers.
one is where we update name in useEffect, that is ,
ConsumerThatUpdates.js
import React, { useContext, useEffect } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatUpdates = () => {
const { changeName } = useContext(NavContext);
useEffect(() => {
changeName("NEW NAME");
}, [changeName]);
return <div>i update on my useeffect</div>;
};
export default ConsumerThatUpdates;
you can update useEffect as per your needs.
another is where we use the name,
ConsumerThatDisplays.js
import React, { useContext } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatDisplays = () => {
const { name } = useContext(NavContext);
return <div>{name}</div>;
};
export default ConsumerThatDisplays;
and finally my App.js looks like this,
App.js
import React from "react";
import "./styles.css";
import { NavProvider } from "./NavContext";
import ConsumerThatDisplays from "./ConsumerThatDisplays";
import ConsumerThatUpdates from "./ConsumerThatUpdates";
export default function App() {
return (
<div className="App">
<NavProvider>
<ConsumerThatDisplays />
<ConsumerThatUpdates />
</NavProvider>
</div>
);
}
hope this helps!!
if you want to know more about how to use context effectively, i recooHow to use React Context effectively

Resources