Passing state from parent to children in React Nextjs - reactjs

I am a beginner in React and I am facing an issue to pass a state from parent to children.
That's my code :
import Footer from './Footer';
import Header from "./Header";
export default function Layout({setPage, children}) {
return <>
<Header setPage={setPage}/>
{children}
<Footer/>
</>
}
At this step, I get the setPage state and I can pass it successfully to the Header component for example. However, {children} don't get it. Is there a way to pass the setPage state to {children} ?
Thanks,
I tried useContext hook but didn't work...

Assuming you are using this Layout component to wrap certain pages content, I don't think you'll need to do this. Follow along:
Probably, you'll use this component as follows:
import { useState } from "React";
import Layout from "../components/Layout";
import OtherComponent from "../components/OtherComponent";
export default function HomePage() {
const [page, setPage] = useState();
return (
<Layout setPage={setPage}>
<OtherComponent setPage={setPage}/>
</Layout>
);
};
Because you'll need to know which children will use that prop. React does have some ways to do this that you asked, but it's discouraged as it fit a super small set of cases.
Keep up learning, good luck!
Edit 1: To complement the usage, you can change what is rendered on the screen using that state like this:
/* previous imports */
import YetOtherComponent from "../components/YetOtherComponent";
export default function HomePage() {
const [page, setPage] = useState(0); /* setting the default inside the parenthesis */
return (
<Layout setPage={setPage}>
{page === 1 /* checking which component should render */
? (<OtherComponent setPage={setPage}/>)
: page === 2
? (<YetOtherComponent setPage={setPage}/>)
: /* As many options as you want */
}
</Layout>
);
};
This way not all of them would display at all times, only when the respective page value is equal to the component that must be shown.

There are several ways to achieve the objective. Described below is one rather fundamental / rudimentary method.
Layout component:
import Footer from './Footer';
import Header from "./Header";
export default function Layout({setPage, children}) {
return <>
<Header setPage={setPage}/>
{children}
<Footer/>
</>
}
Parent / page where layout component is included:
import Layout from './Layout';
export default function SomePage(props) {
const [page, setPage] = useState();
return <>
<Layout setPage={setPage}>
<SomeChild1 setPage={setPage} />
<SomeChild2 setPage={setPage} />
<SomeOtherChild />
</Layout>
</>
}
In this way, the setPage method is passed to each child where it is required. SomeChild1 and SomeChild2 both have the setPage sent as prop. Whereas SomeOtherChild does not require the prop and therefore does not have it.

Related

React Context Consumer Does Not Update Attributes

I can successfully see how React Context updates my values in the Consumer for the text, but it fails to update values inside any attributes. Basically, Consumer seems to keep old default values when inline, generated during the build, while it updates text inside tags just fine.
Here is what I have:
appcontext.js:
import React from 'react';
const AppContext = React.createContext({});
export default AppContext;
layout.js:
import AppContext from "./AppContext"
import PlaceHelper from "./placehelper"
//...
const placeHeloper = PlaceHelper();
const currentPlaceData = placeHeloper.getCurrentPlaceData();
let [placeData, setPlaceData] = useState({ ...currentPlaceData })
//...
return (
<>
<AppContext.Provider value={placeData}>
<main>{children}</main>
</AppContext.Provider>
</>
)
then I use Consumer in the component:
home.js:
import AppContext from "../AppContext"
//...
<div id="content">
<AppContext.Consumer>
{ value =>
<header noupdate={value.banner} style={{backgroundImage: "url(" + value.banner + ")"}}>
{/* UPDATES BELOW SUCCESSFULLY */}
{value.banner}
//...
In an example above, the value.banner gets updated when it is inside the header tag but it fails to be updated when it is inside the style attribute or noupdate attribute. I cannot understand why.

Where to store notification component to be visible in every page ? React custom notification

I have my component for notification
For now this component is only visible in my home page. What I need?
I need to be visible in every route, example if I want to change route to be visible in different route, where to put this component?
Right now this is inside Home page and will be triggered in some case. Work good but when. go to different page is not visible..
Where to set this component to be visible in every page?
I am try inside
const root = document.createElement('div');
document.body.appendChild(root);
render(
<StrictMode>
<NotificationComponent />
<App />
</StrictMode>,
root,
);
but this is not solution....
if you want your notification component to visible in every page, use it or import it in your APP.js
Using Context API, wrap the App with the provider and pass the visibility setter to its children, from there you can update the notification state from any of the provider's children.
Notificationprovider
import { render } from 'less'
import React, { createContext, useState } from 'react'
export const NotificationCtx = createContext()
function NotificationProvider({ children }) {
const [isVisible, setIsVisible] = useState(false)
return <NotificationCtx.Provider value={{ setIsVisible }}>
{children}
{isVisible && <div>Notification</div>}
</NotificationCtx.Provider>
}
index
render(
<NotificationProvider>
<App />
</NotificationProvider>, root
)
App
import { useContext } from 'react'
import { NotificationCtx } from "yourpath/to/provider"
function App() {
const { setIsVisible } = useContext(NotificationCtx)
return <button onClick={() => setIsVisible(true)}></button>
}
You can access the setIsVisible from App's children using useContext.

Extensible layout components in nextjs

I am creating an application in nextjs. I understand that I can generate a layout like the example given in the docs
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
However, I would like to change the contents of the Navbar on a per page basis.
export default function ListPage() {
return {
/** I want to add a secondary nav to the Navbar on this page */
<>
<Navbar><MySecondaryNav></Navbar>
....
</>
}
}
export default function ClientPage() {
return {
/** I want to add a a different secondary nav to the Navbar on this page */
<>
<Navbar><ClientNavbar></Navbar>
....
</>
}
}
I also need the markup to be rendered server-side. Is it possible to achieve this in nextjs?
Yes, you can!
You could use useRouter and then depending on the path render the correct component in NavBar
import { useRouter } from "next/router";
const router = useRouter();
now you should have access to router.pathname, depending on the pathname just render the correct component inside NavBar
#Edit - SSR solution
If you want this SSR, then this is how you do it
in your _app.tsx/jsx you can use getServerSideProps and then get directly resolvedUrl
export default function App({ Component, pageProps }) {
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
export async function getServerSideProps({{req, res, resolvedUrl}) {
return { props: { resolvedUrl } }
}
At this point you should be receiving resolvedUrl and based on that render the required component.
If this wont work for you we might need to setup a codesandbox

Pass state onto {children} React hook

So I have a component that looks like this:
import React, { memo, useState } from "react";
import styles from "./navigation.styles.scss";
const Navigation = ({ children }) => {
const [toggle, toggleState] = useState(false);
return (
<>
<div onClick={() => toggleState(!toggle)}>
<p>Test</p>
</div>
{children}
<style jsx>{styles}</style>
</>
);
};
export default memo(Navigation);
And then I have another component that looks like this:
import React, { memo, useState } from "react";
import styles from "./container.styles.scss";
const Container = ({ children }) => {
const [toggle, toggleState] = useState(false);
return (
<>
<div className={toggle ? "dark-bg" : "dark-bg active"}>
{children}
</div>
<style jsx>{styles}</style>
</>
);
};
export default Container ;
Now, the thing is the {children} of the 1st component is sometimes the 2nd component, and sometimes it's not. Therefore I can't just put the CSS and HTML from the 2ndcomponent into the 1st component - which in turn would fix my problem.
But as you might be able to see, there is an onClick event in the first component. I would like it so that when that is clicked, the state from the click is send to the 2nd component and toggles the className-toggle.
Can this be achieved by doing this, or do I have to set everything up differently ?
And yes, I am quite new to React, so please don't be harsh.
Css
I would look into better methods of applying styling with css. Not sure about your project scope/tools but typically all the css files are imported in the dom root and loaded in there. This avoids creating css files for every component.
Here's 9 ways of implementing css for react.
Passing HTML
In react if you want to render component in another component instead of passing it as a child you should import it as follows.
// replace container path with actual path of Container file
// ex './Container.js'
import Container from 'container_path.js';
Now Rendering the Component is as simple as including it in the html code.
return (
<>
<div className={toggle ? "dark-bg" : "dark-bg active"}>
<Container/>
</div>
</>
);
Here's a Stack Overflow post of users importing components using react + es6 + webpack. More information on importing components is available there.
State management
In react if you have a state that is being accessed by multiple components the standard is to keep the state in the parent component.
This way you can pass the state as a prop to any children components. You can also create a function which updates this state and pass that function as a prop to the children.
ex:
import React, { useState } from "react";
import Container from "./Container.js";
import Navigation from "./Navigation.js"
const Parent = props => {
const [toggle, toggleState] = useState(false);
return (
<div>
<Container toggleState={toggleState} toggle={toggle} />
<Navigation toggleState={toggleState} toggle={toggle} />
</div>
)
}
Before continuing working on your project I would recommend researching functional components vs class components. Here's a helpful article.
Try to wrap second component to function with state from first component as argument.
Wrapper for your second component and using for first component
const putInnerComponent = (stateFromOuterComponent) => <Container toggle={stateFromOuterComponent}/>;
<Navigation children={putInnerComponent}/>
Your first component
import React, { memo, useState } from "react";
import styles from "./navigation.styles.scss";
const Navigation = ({ children }) => {
const [toggle, toggleState] = useState(false);
return (
<>
<div onClick={() => toggleState(!toggle)}>
<p>Test</p>
</div>
{children(toggle)}
<style jsx>{styles}</style>
</>
);
};
export default memo(Navigation);
Your second component
import React, { memo, useState } from "react";
import styles from "./container.styles.scss";
const Container = ({ children, toggle }) => {
//const [toggle, toggleState] = useState(false);
return (
<>
<div className={toggle ? "dark-bg" : "dark-bg active"}>
{children}
</div>
<style jsx>{styles}</style>
</>
);
};
export default Container;

How to add {props.children} to a React component

i have many components which have {props.children} deeply nested inside.
considery DRY principle is there a way to add this using some React pattern.
example
let's say i have two components,
Comp1.js
import React from "react";
const Comp1 = props => {
return (
<div>
<h1>{props.children}</h1>
</div>
);
};
export default Comp1;
Comp2.js
import React from "react";
const Comp2 = props => {
return (
<div>
<div>
<h1>{props.children}</h1>
</div>
</div>
);
};
export default Comp2;
if you see above code we have both Comp1 and Comp2 have line of code {props.children} repeated inside.
what i want now is some function which will add this line of code, something like below,
const addPropsChildrenToComp = (Comp)=>{
return(
(props)=>{
///do somehting here
}
)
}
const Comp1 = props => {
return (
<div>
<h1></h1>
</div>
);
};
Comp1WithPropsChildren = addPropsChildrenToComp(Comp1)
using HOC doesn't work because, in HOC we never modify passed component.
anyway to aceve this.?
to get more idea of my problem see this demo: https://codesandbox.io/s/trusting-http-pd1yu
in there i woul like to see CompWithPropsChildren component render props.children inside it.
I think I see what you're trying to get to, and you can accomplish this just using another component.
import React from "react";
import ChildComp from "./ChildComp";
const Comp1 = props => {
return (
<div>
<ChildComp {...props} />
</div>
);
};
export default Comp1;
import React from "react";
const ChildComp = props => {
return <h1>{props.children}</h1>
}
Assuming your ChildComp has some complex logic you don't want to duplicate, this will make it reusable for you.

Resources