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!
Related
How can navigate?
import ButtonLogReg from "../components/ButtonLogReg"
import { Link } from 'react-router-dom'
import { useContext } from 'react'
import Context from '../components/Context'
import { useNavigate } from 'react-router-dom'
function Launcher() {
log.info('Home -> render')
const { showAlert } = useContext(Context)
const handler = event => {
event.preventDefault()
try(
asyncFunction()
)catch(error)
}
return <main>
<nav>
<h2 >Go?</h2>
<Link to="/Mypath>"<ButtonLogReg onClick={handler}>READY</ButtonLogReg></Link>
<Link to="/"><h2>Back</h2></Link>
</nav>
</main>
}
export default Launcher
Hi everybody, I try to navigate at /Mypath, but i can't do this whit a , how can I do this?
Hope you have setup the route in App.js, then this might help you:
import ButtonLogReg from "../components/ButtonLogReg"
import { useContext } from 'react'
import Context from '../components/Context'
import { useNavigate } from 'react-router-dom'
function Launcher() {
log.info('Home -> render')
const { showAlert } = useContext(Context)
const handler = event => {
event.preventDefault()
try(
asyncFunction()
)catch (error)
}
const navigate = useNavigate();
return <main>
<nav>
<h2 >Go?</h2>
<ButtonLogReg onClick={() => navigate("/Mypath")} >READY</ButtonLogReg>
<h2 onClick={() => navigate("/")} >Back</h2>
</nav>
</main>
}
export default Launcher
I am using the following Languageselector in my next project:
import React, { useState } from 'react'
import i18n from '../src/services/i18n'
import Link from 'next/link'
import { useRouter } from 'next/router'
const LanguageSelector = (props) => {
const [language, setLanguage] = useState(i18n.language);
const router = useRouter()
const handleOnclick = (value) => {
router.push(router.asPath, router.asPath, { locale: value });
};
console.log(router);
return (
<>
<div className={props.className!==undefined&&props.className!==""? ('d-inline-block d-xl-none '+props.className):"d-inline-block d-xl-none "} >
<select onChange={(e)=>{handleOnclick(e.target.value)}} required defaultValue={router.locale}>
<option value='de'>Deutsch</option>
<option value='en'>Enligsh</option>
</select>
</div>
<div className="d-none d-xl-block">
{router.locale==="en"? <div><Link href={router.asPath} locale='de'>DE</Link></div> : <div>DE</div>}
<p>|</p>
{router.locale==="de"? <div><Link href={router.asPath} locale='en'>EN</Link></div> : <div>EN</div>}
</div>
</>
)
}
export default LanguageSelector
_app.js
This does work fine until the visitor is on an URL, that has a # parameter. The URL in browser bar still changes but the language stays the same. How can I change the language on URLs with hash?
EDIT: This is how I am using translation
import Layout from '../components/Layout'
import { appWithTranslation } from 'next-i18next';
import React, {useState, useEffect} from 'react';
import { useRouter } from 'next/router'
const app = ({Component, pageProps })=> {
const router = useRouter()
function updateSchnellzugriffe(value, mobileValue){
setSchnellzugriffe(value);
setMobileSchnellzugriffe(mobileValue);
}
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default appWithTranslation(app)
And this is an example of pageComponent
import React, {useEffect, useState} from 'react'
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
const home = (props) => {
const { t } = useTranslation(['common','home']);
return (
<h1 >{t("home:headline")}</h1>
)
};
export async function getStaticProps({ locale }){
return {
props: {
...await serverSideTranslations(locale, ['common','home']),
},
}
}
export default home
I've been struggling with my first solo proyect due to my lack of experience, here is the trouble I have:
I need to set a Link component (of react-router-dom) which takes me to another new page when I clicked the button. Thought that selection I need to pass to the link (and the context) the id so I can fetch the data from the API, but I cannot get an idea of how to make it works. This is the button link component:
Model.jsx
import React, { useState, useContext } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom';
import FileModel from "../FileModel/FileModel";
import { ModelsContext } from "../../context/ModelsContext";
const Model = ({modelo}) => {
const { id, name, year, price, photo } = modelo;
const { guardarModelo } = useContext(ModelsContext);
const [display, setDisplay] = useState("btn-notdisplayed");
const showButton = e => {
e.preventDefault();
setDisplay("btn-displayed");
};
const hideButton = e => {
e.preventDefault();
setDisplay("btn-notdisplayed");
};
return (
<div
className="card"
onMouseEnter={e => showButton(e)}
onMouseLeave={e => hideButton(e)}
>
<div className="card-body">
<p className="card-name">{name}</p>
<p className="card-yearprice">{year} | $ {price}</p>
</div>
<img src={`https://challenge.agenciaego.tech${photo}`} className="card-image" alt={`Imagen de ${name}`} />
<Router>
<button
type="button"
className={display}
onClick={() => {
guardarModelo(modelo);
}}
><Link to={`/models/${modelo.id}`}>Ver Modelo</Link>
</button>
<Switch>
<Route exact path={`/models/${modelo.id}`} component={FileModel} />
</Switch>
</Router>
</div>
);
}
export default Model;
Then I obtained the data from a context:
ModelsContext.jsx
import React, { createContext, useState, useEffect } from 'react';
export const ModelsContext = createContext();
const ModelsProvider = (props) => {
//State de modelos
const [ modelo, guardarModelo ] = useState({});
const [ modelos, guardarModelos ] = useState([]);
const [ allModelos, guardarAllModelo ] = useState([]);
//Cargar un modelo
useEffect(() => {
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
const api2 = await fetch(`https://challenge.agenciaego.tech/models/${id}`);
const modelo = await api2.json();
guardarAllModelo(modelos);
guardarModelos(modelos);
guardarModelo(modelo);
}
consultarAPI()
}, []);
return (
<ModelsContext.Provider
value={{
allModelos,
modelo,
modelos,
guardarModelo,
guardarModelos
}}
>
{props.children}
</ModelsContext.Provider>
)
}
export default ModelsProvider;
Finally, I got the App.js from which I route the principal component, the idea Is to get with the Link to the new component called "FileModel.jsx" as a child component an so maintains the Navbar component.
App.js
import React from "react";
import Navbar from "./components/Nav/Navbar";
import Models from "./components/Models/Models";
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import Logo from "./assets/img/logo.png";
import ModelsProvider from "./context/ModelsContext";
import ModelFooter from "./components/Models/ModelFooter";
function App() {
return (
<ModelsProvider>
<Router>
<nav className="navbar">
<img src={Logo} className="logo" alt="Ego Logo" />
<div className="menu-container">
<Link to={'/models'} className="menu-items">Modelos</Link>
<a className="menu-items">Ficha de modelo</a>
</div>
<div className="bottom-line"></div>
</nav>
<Switch>
<Route exact path='/models' component={Models} />
</Switch>
</Router>
<Navbar />
<ModelFooter />
</ModelsProvider>
);
}
export default App;
This is the fileModel.jsx so far:
import React, { useContext } from 'react';
import Navbar from "../Nav/Navbar";
import { ModelsContext } from "../../context/ModelsContext";
const FileModel = () => {
const { modelo } = useContext(ModelsContext);
console.log(modelo.id);
return (
<Navbar />
);
}
export default FileModel;
I hope to have explained my issue clear, and thanks so much to all you caring people!
Cheers!
Ps: Maybe you can find some things to refactor (I will need to check my code later), If you find something like that any help will be appreciated!
UPDATE
Due to Linda recommendation I merged the two contexts into one, and changed some lines of the code I wrote before, I cannot set the state to pass the solo model to the fileModel component and the Link still not working, I was thinking in a functions that can do it, I made another state, a single modelo, but when I click the button I got an error and undefined, because Idk how to set in the state the Id and so pass it to the API call, the terminal says that id in const api2 = await fetch(https://challenge.agenciaego.tech/models/${id}); is not defined.
I FINALLY SOLVED MY PROBLEM!!! I had to rellocate some elements between my component to make the api context take the id and pass it to the FileModel component, and change the Link router to the App.js, this is how I get to the solution:
App.js
import React from "react";
import Navbar from "./components/Nav/Navbar";
import Models from "./components/Models/Models";
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import Logo from "./assets/img/logo.png";
import ModelsProvider from "./context/ModelsContext";
import ModelFooter from "./components/Models/ModelFooter";
import FileModel from "./components/FileModel/FileModel";
function App() {
return (
<ModelsProvider>
<Router>
<nav className="navbar">
<img src={Logo} className="logo" alt="Ego Logo" />
<div className="menu-container">
<Link to={'/models'} className="menu-items">Modelos</Link>
<Link to={`/models/1`} className="menu-items">Ficha del Modelo</Link>
</div>
<div className="bottom-line"></div>
</nav>
<Switch>
<Route exact path='/models' component={Models} />
<Route exact path='/models' component={Models} />
</Switch>
</Router>
<Navbar />
<FileModel />
<ModelFooter />
</ModelsProvider>
);
}
export default App;
Modelscontext.jsx
import React, { createContext, useState, useEffect } from 'react';
export const ModelsContext = createContext();
const ModelsProvider = (props) => {
//State de modelos
const [ modelo, guardarModelo ] = useState([]);
const [ modelos, guardarModelos ] = useState([]);
const [ allModelos, guardarAllModelo ] = useState([]);
const { id } = modelo;
//Cargar un modelo
useEffect(() => {
const consultarAPI = async () => {
const api = await fetch("https://challenge.agenciaego.tech/models");
const modelos = await api.json();
const api2 = await fetch(`https://challenge.agenciaego.tech/models/${id}`);
const modelo = await api2.json();
guardarAllModelo(modelos);
guardarModelos(modelos);
guardarModelo(modelo);
}
consultarAPI()
}, [modelo.id]);
return (
<ModelsContext.Provider
value={{
allModelos,
modelo,
modelos,
guardarModelo,
guardarModelos
}}
>
{props.children}
</ModelsContext.Provider>
)
}
export default ModelsProvider;
Model.js
import React, { useState, useContext } from 'react';
import { Link } from 'react-router-dom';
import { ModelsContext } from "../../context/ModelsContext";
const Model = ({modelo}) => {
const { name, year, price, photo } = modelo;
const { guardarModelo } = useContext(ModelsContext);
const [display, setDisplay] = useState("btn-notdisplayed");
const showButton = e => {
e.preventDefault();
setDisplay("btn-displayed");
};
const hideButton = e => {
e.preventDefault();
setDisplay("btn-notdisplayed");
};
return (
<div
className="card"
onMouseEnter={e => showButton(e)}
onMouseLeave={e => hideButton(e)}
>
<div className="card-body">
<p className="card-name">{name}</p>
<p className="card-yearprice">{year} | $ {price}</p>
</div>
<img src={`https://challenge.agenciaego.tech${photo}`} className="card-image" alt={`Imagen de ${name}`} />
<Link to={`/models/${modelo.id}`}><button
type="button"
className={display}
onClick={() => {
guardarModelo(modelo);
}}
>Ver Modelo
</button></Link>
</div>
);
}
export default Model;
FileModel.js (so far...)
import React, { useContext } from 'react';
import Navbar from "../Nav/Navbar";
import { ModelsContext } from "../../context/ModelsContext";
const FileModel = () => {
const { modelo } = useContext(ModelsContext);
const { photo, name } = modelo;
return (
<>
<Navbar />
<section>
<img src={`https://challenge.agenciaego.tech${photo}`} alt={`Imagen de ${name}`}/>
</section>
</>
);
}
export default FileModel;
Thanks to Linda for giving me the input to refactoring the context! cheers!
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.
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;