I have a parent functional component and i need to pass props and state to a child functional component, i have managed to pass only one of theme (props or state), the code below displays the fetched data, firstly i've been using const Footer = ({name, adresse, phone}) => {} and then i've replaced it with const Footer = (props) => {} i thought i can pass them this way!!
{props.colorScheme} is accessible in App.js but not in Footer component, should i use context API to pass the props?
FYI, here is my index.js :
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const colorScheme = root_el.getAttribute("color-scheme");
ReactDOM.render(
<App customBackground={customBackground} colorScheme={colorScheme} />,
root_el
);
My App component
import React, {useEffect, useState, Suspense, lazy } from 'react';
import axios from 'axios';
import Footer from "./components/Footer";
const App = (props) => {
const [infos, setInfos] = useState({});
useEffect( () => {
loadData();
}, []);
const loadData = () => {
axios.get(`https://api`)
.then((res) => {
console.log(res.data);
const infs = setInfos(res.data);
});
}
return (
<div>
<Footer name={infos.name} adresse={infos.adresse} phone= {infos.phone}
</div>
)
};
export default App;
My child component :
import React from 'react';
const Footer = (props) => {
const {name, adresse, phone} = props;
return (
<div>
<h3>{props.colorScheme}</h3>
<span>{name}<span>
<span>{adresse}<span>
<span>{phone}<span>
</div>
)
}
export default Footer;
You can continue to pass the props down to the footer component or you can, as you point out, use a context. Passing the colorScheme via the props is shown below.
Note: Your code was displaying the colorScheme as an h3 in the Footer and I left that as is.
Updated App Component:
import React, {useEffect, useState, Suspense, lazy } from 'react';
import axios from 'axios';
import Footer from "./components/Footer";
const App = (props) => {
const [infos, setInfos] = useState({});
const { colorScheme } = props;
useEffect( () => {
loadData();
}, []);
const loadData = () => {
axios.get(`https://api`)
.then((res) => {
console.log(res.data);
const infs = setInfos(res.data);
});
}
// Footer tag below was missing the tag's closing
// Added colorScheme prop
return (
<div>
<Footer
colorScheme={colorScheme}
name={infos.name}
adresse={infos.adresse}
phone= {infos.phone}/>
</div>
)
};
export default App;
Updated Footer
import React from 'react';
const Footer = (props) => {
const {name, adresse, phone, colorScheme} = props;
return (
<div>
<h3>{colorScheme}</h3>
<span>{name}<span>
<span>{adresse}<span>
<span>{phone}<span>
</div>
)
}
export default Footer;
You can also create a new Context using createContext and useContext so that you can have a single way for all your components to access it. You won't have to pass the color scheme through props. You may want to do both so that you have a global set of default colors and then a prop that lets you override them.
Related
I am trying to send props to component with NavLink.
<NavLink to={{pathname :"/wind", state:{from: "This is my props"}}} >Wind</NavLink>
and I want to get it in this component,
import React from 'react';
import { useLocation} from "react-router-dom"
const Wind = () =>{
const location = useLocation();
const {from} = location.state;
console.log({from})
return (
<div className="Wind">
<h1>Wind</h1>
</div>
);
}
export default Wind;
unfortunatelly this give me result
so I try it like this
import React from 'react';
const Wind = (props) =>{
//console.log(props.location.state)
return (
<div className="Wind">
<h1>Wind</h1>
</div>
);
}
export default Wind;
console log in ths case is undefined.
I donĀ“t know why it is not working any ideas?
In the new version, the state is passed as a separate prop. Try using
<NavLink to={{pathname :"/wind"}}
state={{from: "This is my props"}} >Wind</NavLink>
And the component try fetching like this
import React from "react";
import { useLocation } from "react-router-dom";
const Wind = () => {
const location = useLocation();
const { state } = location;
console.log(state.from);
return (
<div className="Wind">
<h1>Wind</h1>
</div>
);
};
export default Wind;
Showing white blank screen with the use of context api in React js
I'm running my code which has no errors, but when I take a trip to localhost:3000
I get a blank screen.
These are the React js files in the project, If I don't use the Context-API, coding is doing great.
App.js
import "./App.css";
import { UserContextProvider } from "./contexts/user";
import { Home } from "./pages";
function App() {
return (
<div className="app">
<UserContextProvider>
<Home />
</UserContextProvider>
</div>
);
}
export default App;
user.js
import { createContext, useState } from "react";
export const UserContext = createContext();
export const UserContextProvider = (props) => {
const [user, setUser] = useState(null); // user value can be used anywhere
return (
<UserContext.Provider value={{user: [user,setUser]}}>
{props.childern}
</UserContext.Provider>
);
};
SignInBtn.js
import React,{useContext} from "react";
import "./style.css";
import GoogleIcon from "#mui/icons-material/Google";
import { signInWithGoogle } from "../../services/auth";
import { UserContext } from "../../contexts/user";
const icon = {
fontSize: 14,
};
function SignInBtn() {
const [user, setUser] = useContext(UserContext).user;
const signInBtnClick = async () =>{
let userBySignIn = await signInWithGoogle()
if(userBySignIn) setUser(userBySignIn)
}
return (
<div className="signInBtn" onClick={signInBtnClick}>
<div className="btn">
<p>Signin with </p>
<GoogleIcon style={icon}/>
</div>
</div>
);
}
export default SignInBtn;
first of everything make sure your SignInBtn Component is children of Context Provider
secondly
Remove the .user from here
const [user, setUser] = useContext(UserContext).user; in SignInBtn.js Where you are taking value from UserContext.
You can do this way
const {user} = useContext(UserContext);
const [user, setUser] = user
Solution <>{props.children}</>
You should enclose {props.childern} in Fragments. or any other tags that do not return null in any case.
Refer to this. https://codesandbox.io/s/divine-shape-6rxws?file=/src/user.js:331-353 for the exact solution
you should import React in user.js
it will work
So I want to pass my navbar ref as a props to a page. But I don't know how to pass it, I migrated my project from cra to next, I'm basically a newbie, please if there's better way to do this, please tell me. Thank you!.
Here is my navbar component code
import React, { Fragment, useState, useEffect, useRef } from "react";
import Link from "next/link";
const NavBar = ({ navBarRefProps }) => {
const navBarRef = useRef();
useEffect(() => {
navBarRefProps(navBarRef.current);
},[navBarRef])
return(
<Fragment>
<nav ref={navBarRef}>
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
</ul>
</nav>
</Fragment>
)
}
export default NavBar;
Here is my Layout component.
import React, { Fragment, useState, useEffect, useRef } from "react";
import { useRouter } from 'next/router';
import NavBar from "./../NavBar";
import MidNavBar from "./../MidNavBar";
import Banner from "./../Banner";
const Layout = ({ children, navBarRefProps }) => {
const navBarRef = useRef();
const router = useRouter();
const navBarRefPropsHandle = (props) => {
navBarRef.current = props.current;
navBarRefProps(navBarRef.current);
}
return(
<Fragment>
<div className="wrapper">
<NavBar navBarRefProps={ navBarRefPropsHandle } />
<div className="inner_wrapper">
<Banner/>
{
router.pathname !== "/" ?
<MidNavBar midNavBarRefProps = {midNavBarRefPropsHandle} dummyRefProps={dummyRefPropsHandle}/>
:
""
}
{ children }
</div>
</div>
</Fragment>
)
}
Here is my _app.js file
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import React , { useState, useRef, useEffect } from "react";
import Layout from "./../components/Layout";
import './../styles/css/index.css';
function MyApp({ Component, pageProps }) {
const navBarRef = useRef();
const setNavBarRefHandle = (props) => {
navBarRef.current = props;
}
return(
<Layout navBarRefProps={ setNavBarRefHandle }>
<Component {...pageProps} navBarRefProps={ navBarRef.current } />
</Layout>
)
}
export default MyApp;
Here is my home page or index.js file, where I want to use the navbar ref
import React, { Fragment, useEffect, useRef } from "react";
import ImageSlider from "./../components/ImageSlider ";
import About from "./../components/About";
import MidNavBar from "./../components/MidNavBar";
// I thought I could access the navBarRefProps if I passed it on the <Component> in _app.js but it's undefined
const Home = ({ navBarRefProps }) => {
const navBarRef = useRef();
const midNavBarRef = useRef();
useEffect(() => {
navBarRef.current = navBarRefProps;
},[navBarRefProps])
const stickMidNavBarHandle = () => {
console.log(navBarRef.current);
let navBarRefSize = navBarRef.offsetHeight;
}
useEffect(() => {
window.addEventListener("scroll",stickMidNavBarHandle );
return () => {
window.removeEventListener("scroll",stickMidNavBarHandle);
}
},[])
const midNavBarRefPropsHandle = (props) => {
midNavBarRef.current = props.current;
}
return(
<Fragment>
<ImageSlider/>
<MidNavBar midNavBarRefProps = {midNavBarRefPropsHandle}/>
<About />
</Fragment>
)
}
export default Home;
This is my file structure
root folder
.next
components
NavBar
-index.js
Layout
-index.js
MidNavBar
-index.js
pages
_app.js
index.js
about.js
about
-missionVision.js
public
images
videos
fonts
styles
Thank you!
In a Gatsby project I have a header component which is persistent on every page. The header has a modal to display the navigation. I need to set the isOpen state to false whenever the route changes so that the nav modal closes. Since the route can change not just by clicking links in the modal but also by using the back button on the browser I don't want use an event on the links to close the modal.
In Gatsby I can use the onRouteUpdate in gatsby-browser.js to detect route changes and this works well. But I need to pass the event to my component and this is where I am having difficulty. I have simplified the code below to show the setup.
gatsby-browser.js:
import React from "react"
import Layout from "./src/components/layout"
export const wrapPageElement = ({ element, props }) => {
return <Layout {...props}>{element}</Layout>
}
export const onRouteUpdate = () => {
console.log("onRouteUpdate") // this works
}
layout.js:
import React from "react"
import Header from "./header"
import Footer from "./footer"
const Layout = ({ children }) => (
<>
<Header />
<main>
{children}
</main>
<Footer />
</>
)
export default Layout
header.js:
import React, { useState } from "react"
const Header = () => {
const [isOpen, setIsOpen] = useState(null)
const toggleState = ({ props }) => {
let status
if (props) status = props.status
else status = !isOpen
setIsOpen(status)
}
return (
<header>
<div>This is the header</div>
<button onClick={toggleState}>Toggle Open/Close</button>
<button onClick={toggleState({ status: false })}>This will always close</button>
/* logic here uses isOpen state to determine display */
</header>
)
}
export default Header
My preferred approach to solving this is to use the undocumented globalHistory from #reach/router, which Gatsby uses.
import { globalHistory } from '#reach/router'
useEffect(() => {
return globalHistory.listen(({ action }) => {
if (action === 'PUSH') setIsOpen(false)
})
}, [setIsOpen])
Now whenever you switch routes, the above effect will fire.
Source.
I have come up with a solution to my own question so I thought I would share. Any comments/improvements are always welcome.
First, we do not need to use "onRouteUpdate" in gatsby-broser.js so let's remove that:
/* gatsby-browser.js */
import React from "react"
import Layout from "./src/components/layout"
export const wrapPageElement = ({ element, props }) => {
return <Layout {...props}>{element}</Layout>
}
Then, in layout.js make sure to pass the location to the header:
/* layout.js */
import React from "react"
import Header from "./header"
import Footer from "./footer"
const Layout = ({ children, location }) => (
<>
<Header location={location} />
<main>
{children}
</main>
<Footer />
</>
)
export default Layout
Finally, in header.js the location is stored on a reference to the header element by utilizing the useRef hook. The useEffect hook will get fired on route changes so we can use that to compare:
/* header.js */
import React, { useState, useEffect, useRef } from "react"
const Header = () => {
const [isOpen, setIsOpen] = useState(null)
const myRef = useRef({
location: null,
})
useEffect(() => {
// set the location on initial load
if (!myRef.current.location) myRef.current.location = location
// then make sure dialog is closed on route change
else if (myRef.current.location !== location) {
if (isOpen) toggleState({ status: false })
myRef.current.location = location
}
})
const toggleState = ({ props }) => {
let status
if (props) status = props.status
else status = !isOpen
setIsOpen(status)
}
return (
<header ref={myRef}>
<div>This is the header</div>
<button onClick={toggleState}>Toggle Open/Close</button>
<button onClick={toggleState({ status: false })}>This will always close</button>
</header>
)
}
export default Header
Hopefully this helps anyone looking for similar functionality.
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;