I have 3 components (CompA, CompB, CompC) I want to send data to from CompA to CompC but it is showing error Render is not a function updateContextConsumer. I am using functional components
import React,{ createContext } from 'react';
import './App.css';
import CompA from './ContextApi/CompA';
const FirstName = createContext();
function App() {
return (
<>
<FirstName.Provider value={"JD"}>
<CompA/>
</FirstName.Provider>
</>
);
}
export default App;
export {FirstName};
import React from 'react';
import CompB from './CompB';
const CompA = () =>{
return(
<CompB/>
)
}
export default CompA;
import React from 'react';
import CompC from './CompC';
const CompB = () =>{
return(
<CompC/>
)
}
export default CompB;
import React from 'react';
import {FirstName} from '../App';
const CompC = () =>{
return(
<>
<FirstName.Consumer> {fname=>(<h1>{fname}</h1>) } </FirstName.Consumer>
</>
)
}
export default CompC;
Image of error is here
enter image description here
I believe the issue is the spaces between the end of <FirstName.Consumer> and the { and the } and </FirstName.Consumer>. The following does not work with those spaces left in:
const FirstName = React.createContext();
const CompA = () =>{
return (
<CompB/>
)
};
const CompB = () =>{
return (
<CompC/>
)
};
const CompC = () => {
return(
<React.Fragment>
<FirstName.Consumer>{ (fname) => (<h1>{fname}</h1>) }</FirstName.Consumer>
</React.Fragment>
)
};
function App() {
return (
<React.Fragment>
<FirstName.Provider value={"JD"}>
<CompA/>
</FirstName.Provider>
</React.Fragment>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
You could also put the { (fname) => (<h1>{fname}</h1>) } part on a separate line like shown here: https://reactjs.org/docs/context.html#contextconsumer
Related
I have a hook, and 2 components. Component App.js has a function that changes the state in hook, but the value is not updated in Component New.js, why? I think I've missed something but can't figure it out.
App.js
export const useToggle = () => {
const [onOff, setOnOff] = useState(false);
return [onOff, () => setOnOff((prev) => !prev)];
};
export default function App() {
const [onOff, setOnOff] = useToggle();
return (
<div className="App">
<h1>{onOff.toString()}</h1>
<button onClick={setOnOff}>toggle</button>
</div>
);
}
New.js
import { useToggle } from "./App.js";
export default function New() {
const [onOff] = useToggle();
return (
<div className="App">
<hr />
<h1>NEW:</h1>
<pre>{onOff.toString()}</pre>
</div>
);
}
https://codesandbox.io/s/musing-fire-rjude?file=/src/App.js
Each useToggle hook is its own entity with its own state. The useToggle that you are toggling in App isn't the same useToggle that is rendered/used in New.
This means they are toggled independently of any other hooks and state. They don't share "state".
If you are wanting to create a useToggle hook that does have shared state then I would suggest implementing it via a React context and the useContext hook so each useToggle hook can toggle the same shared state held in the context.
Update
Global useToggle hook.
togglecontext.js
import { createContext, useContext, useState } from 'react';
export const ToggleContext = createContext([false, () => {}]);
const ToggleProvider = ({ children }) => {
const [onOff, setOnOff] = useState(false);
const toggle = () => setOnOff(t => !t);
return (
<ToggleContext.Provider value={[onOff, toggle]}>
{children}
</ToggleContext.Provider>
);
}
export const useToggle = () => useContext(ToggleContext);
export default ToggleProvider;
index - provide the context
...
import ToggleProvider from "./toggle.context";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<ToggleProvider>
<App />
<New />
</ToggleProvider>
</StrictMode>,
rootElement
);
App
import "./styles.css";
import { useToggle } from "./toggle.context";
export default function App() {
const [onOff, setOnOff] = useToggle();
return (
<div className="App">
<h1>{onOff.toString()}</h1>
<button onClick={setOnOff}>toggle</button>
</div>
);
}
New
import { useToggle } from "./toggle.context";
export default function New() {
const [onOff] = useToggle();
return (
<div className="App">
<hr />
<h1>NEW:</h1>
<pre>{onOff.toString()}</pre>
</div>
);
}
Note that the only thing that changed in the App and New components was the import, where the useToggle hook is defined.
Hey how are you? i'm using nextjs / styled components and i would like to change a string via props. I made a layout component with a main and it has an props to make it dinamic per page
Page 'prueba.js'
import React from 'react';
import Layout from '../components/Layout/Layout';
const prueba = () => {
const setNewBackground = 'green';
console.log(setNewBackground)
return (
<Layout setNewBackground={setNewBackground}>
<p>holaaaaa</p>
</Layout>
);
}
export default prueba;
Layout.js component
import React,{useState} from 'react';
import Main from './Main';
const Layout = props => {
const [background, setBackground] = useState('blue')
const setNewBackground = () => {
setBackground (background);
}
return (
<>
<Main newBackground={background}>
{props.children}
</Main>
</>
);
}
export default Layout;
And Main.js component
import styled from '#emotion/styled';
const Main = styled.main`
background:${props => props.newBackground};
height:100vh;
width:100%;
`;
export default Main;
I check it in console but it shows me undefined. Wheres the error :(? thanks and have a good year!
You don't need to create a state for that. You can use only the color prop you passed to the Layout.
Prueba.js
import React from 'react';
import Layout from '../components/Layout/Layout';
const prueba = () => {
return (
<Layout backgroundColor='green'>
<p>holaaaaa</p>
</Layout>
);
}
export default prueba;
Layout.js
import React,{useState} from 'react';
import Main from './Main';
const Layout = ({ backgroundColor, chidlren }) => {
return (
<Main backgroundColor={backgroundColor}>
{children}
</Main>
);
}
export default Layout;
Main.js
import styled from '#emotion/styled';
const Main = styled.main`
background:${({ backgroundColor }) => backgroundColor || 'your default color'};
height:100vh;
width:100%;
`;
export default Main;
I started to learn React, I'm trying to retrieve data from api, the data is an object with the fields of base, date & rates, without any problem I can print and logout base & date but rates which is an object not.
console.log gives undefined, when trying to iterate is obviously that the object does not exist but in DevTools i can see normal data
Thank you for your help and greetings
Context:
export const ExchangeProvider = props => {
const [lastestExchanges, setLastestExchanges] = useState({})
const fetchLastestExchange = async () => {
try {
await fetch(`https://api.exchangeratesapi.io/latest`).then(data => data.json()).then(data => setLastestExchanges(data))
} catch (err) {
console.log(err)
}
}
useEffect(() => {
fetchLastestExchange()
}, [])
return (
<ExchangeContext.Provider value={[lastestExchanges, setLastestExchanges]}>
{props.children}
</ExchangeContext.Provider>
)
}
Usage:
import React, {useState, useContext} from "react";
import {ExchangeContext} from "../ExchangeContext";
function HomeView() {
const [lastestExchange, setLastestExchange] = useContext(ExchangeContext)
console.log(lastestExchange)
return (
<div className="container">
<p>{lastestExchange.base}</p>
<p>{lastestExchange.date}</p>
{/*<p>{lastestExchange.rates['PLN']}</p>*/}
<ul>
{/*{Object.keys(lastestExchange.rates).map(key => <li>{lastestExchange.rates[key]}</li>)}*/}
</ul>
</div>
)
}
export default HomeView
Provider usage:
import React from 'react';
import HomeView from "./Views/HomeView";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import {ExchangeProvider} from "./ExchangeContext";
function App() {
return (
<ExchangeProvider>
<div className="App container w-full flex h-full">
<Router>
<Switch>
<Route path="/">
<HomeView/>
</Route>
</Switch>
</Router>
</div>
</ExchangeProvider>
);
}
export default App;
You can use react context simpler like this :
// src/ThemeContext.js
import React from 'react';
const ThemeContext = React.createContext(null);
export default ThemeContext;
// src/ComponentA.js
import React from 'react';
import ThemeContext from './ThemeContext';
const A = () => (
<ThemeContext.Provider value="green">
<D />
</ThemeContext.Provider>
);
// src/ComponentD.js
import React from 'react';
import ThemeContext from './ThemeContext';
const D = () => (
<ThemeContext.Consumer>
{value => (
<p style={{ color: value }}>
Hello World
</p>
)}
</ThemeContext.Consumer>
);
I have a problem whit ThemeProvider in my nextjs project.
I wounder what is the best way to pass data to the theme.
In my getInitialProps inside the /pages/index I get the setting data, and the ThemeProvider tag is in /pages/_app.js wrapper <Component />.. how can I pass the data from index to _app? any idea?
Thanksss!!
here is my code:
/pages/_app.js
import App from "next/app";
import React from "react";
import Head from "next/head";
/* Styles */
import { ThemeProvider } from "styled-components";
import theme from "../styles/theme/primary";
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<>
<ThemeProvider theme={theme}> // Here ThemeProvider whit theme
<Header />
<Container>
<Component {...pageProps} />
</Container>
<Footer />
</ThemeProvider>
</>
);
}
}
export default MyApp;
pages/index.js
import React, { useState, useEffect } from "react";
/* Others */
import { getData } from "../helper/api";
const Index = props => {
const res = props.data;
return (
<>
<Home data={res} />
</>
);
};
Index.getInitialProps = async ({ res }) => {
try {
let req = await getData(); // Here I get the settings.
return { data: req };
} catch (e) {
console.log(`Error: ${e}`);
return { data: null};
}
};
export default Index;
When attempting to pass custom props from layout to children, I am receiving the following: TypeError: props.children is not a function
Layout (functional component summary)
import React, { useState } from 'react'
import { useStaticQuery, graphql } from 'gatsby'
export default (props) => {
const {site} = useStaticQuery(
graphql`
{
site {
siteMetadata {
title
}
}
}
`
)
const globals = {title: site.siteMetadata.title}
return (
<>
{props.children({...props, ...globals})}
</>
)
}
Child (also a functional component)
import React from "react"
import Layout from '../components/layout'
export default () => {
return (
<Layout>
<main>
<h1>*site title will go here</h1>
</main>
</Layout>
)
}
Render function Pattern
To use render function pattern you need to modified your child component as
import React from "react"
import Layout from '../components/layout'
export default () => {
return (
<Layout>
{props => (<main>
<h1>{props.title}</h1>
</main>)}
</Layout>
)
}