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;
Related
I want to change the thumb color of the scroll bar depending on the page I am on. Currently, I am using this script that I had made:
// /scripts/scrollbar.js
export default function updateScrollbar(thumbColor) {
const html = document.getElementsByTagName("html")[0];
html.style.setProperty("--thumb", thumbColor);
}
And if I am on some page, I have the following code to update the scroll bar thumb color
// /pages/Comp.js
import { useEffect } from "react";
import updateScrollbar from "../../scripts/scrollbar";
export default function Comp() {
useEffect(() => {
updateScrollbar("var(--purple)");
}, []);
return (
<p>hi</p>
);
}
This works fine. I visit a page and the scrollbar color updates.
I don't want to use to this scrollbar.js script though. So I tried the following:
// /Layouts/Layout.js
import { createContext, useState } from "react";
export const AppContext = createContext();
export default function Layout({ children }) {
const [thumbColor, setThumbColor] = useState("var(--white)");
const context = { thumbColor, setThumbColor };
return (
<AppContext.Provider value={context}>
{ children }
</AppContext.Provider>
)
}
I have created a context and passed thumbColor and setThumbColor as Provider's value.
// /pages/_app.js
import Layout from '../Layouts/Layout';
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default MyApp;
The layout wraps the _app.js.
// /pages/Comp.js
import { useContext, useEffect } from "react";
import { AppContext } from "../../Layouts/Layout";
export default function Comp() {
const { setThumbColor } = useContext(AppContext);
useEffect(() => {
setThumbColor("var(--purple)");
}, []);
return (
<p>hi</p>
);
}
I get the set function from AppContext and want to update the thumbColor state from here.
import Document, { Html, Head, Main, NextScript } from "next/document";
import { AppContext } from "../Layouts/Layout";
class MyDocument extends Document {
static contextType = AppContext;
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps }
}
render() {
let temp = this.context; // this is undefined
return (
<Html style={{ "--thumb": temp.thumbColor }} lang="en">
<Head>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument;
Since context is undefined, I am not able to do anything. What am I doing wrong ?
Thanks for helping !
Edit:
I was not using
import Link from 'next/link';
in my Header.tsx component.
Now it works.
Don't know what I am doing wrong right here.
I try to make global state (to indicate if user is logged in or not) that just flows through the pages and I try to do it with react's hook useContext.
It is not working like how I would like to make it work. When I toggleLogged and go to another page, the context has default value and not the changed one.
I think the problem I am facing is something really small or a fundemantal thing that I just can't see.
Here is how my UserContext.ts file looks:
import { createContext, useContext } from 'react';
type userContextType = {
isLogged: boolean;
toggleLogged: () => void;
};
const userContextDefault: userContextType = {
isLogged: false,
toggleLogged: () => {},
};
export const UserContext = createContext(userContextDefault);
export function useUserContext() {
return useContext(UserContext);
}
Here is my Layout.tsx:
import React, { useState } from 'react';
import Header from './Header';
const Layout = (props: any) => {
const { children } = props;
return (
<div className='container'>
<Header />
{children}
</div>
);
};
export default Layout;
And lastly here is my _app.tsx:
import type { AppContext, AppProps } from 'next/app';
import Layout from '../components/Layout';
import { useState } from 'react';
import { UserContext } from '../components/UserContext';
import '../styles/globals.css';
const MyApp = ({ Component, pageProps }: AppProps) => {
const [isLogged, setIsLogged] = useState(true);
const toggleLogged = () => {
setIsLogged((isLogged) => !isLogged);
};
return (
<UserContext.Provider value={{ isLogged, toggleLogged }}>
<Layout>
<Component {...pageProps} />;
</Layout>
</UserContext.Provider>
);
};
export default MyApp;
Thanks for any help in advance.
You need to return the provider in the context, and then reference the instance in _app.js.
Here's my AuthContext (as an example). Don't worry about the specific code, but use my implementation as the foundation, and you'll be up and running in no time!
import { createContext, useContext, useEffect, useState } from 'react'
import { auth } from '../firebase'
const AuthContext = createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState(true)
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
function signOut() {
return auth.signOut();
}
function signUp(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function getUser() {
return auth.currentUser
}
function isAdmin() {
return auth.currentUser.getIdTokenResult()
.then((idTokenResult) => {
if (!!idTokenResult.claims.admin) {
return true
} else {
return false
}
})
}
function isEditor() {
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
getUser,
login,
signOut,
signUp
}
return (
<AuthContext.Provider value={value}>
{ !loading && children }
</AuthContext.Provider>
)
}
My _app.js file:
import '../styles/globals.scss'
import { motion, AnimatePresence } from 'framer-motion'
import { useRouter } from 'next/router'
import Header from '../components/Header'
import Footer from '../components/Footer'
import { AuthProvider } from '../contexts/AuthContext'
import { CartProvider } from '../contexts/CartContext'
import { ThemeProvider } from '#material-ui/core'
import theme from '../styles/theme'
export default function App({ Component, pageProps }) {
const router = useRouter()
return(
<AnimatePresence exitBeforeEnter>
<CartProvider>
<AuthProvider>
<ThemeProvider theme={theme}>
<Header />
<motion.div key={router.pathname} className="main">
<Component { ...pageProps } />
<Footer />
</motion.div>
</ThemeProvider>
</AuthProvider>
</CartProvider>
</AnimatePresence>
)
}
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 need to use dispatch Context API methods in _app.js.
The main limitation is that I use React hooks along with Context API, since _app.js is a Class, I can't use hooks within it.
My code:
// store.js
import React, { createContext, useContext, useReducer } from "react";
import mainReducer from "../store/reducers";
const AppStateContext = createContext();
const AppDispatchContext = createContext();
const initialState = {
filters: {
diet: {
selected: []
}
}
};
const useAppState = () => useContext(AppStateContext);
const useAppDispatch = () => useContext(AppDispatchContext);
const useApp = () => [useAppState(), useAppDispatch()];
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(mainReducer, initialState);
return (
<AppStateContext.Provider value={state}>
<AppDispatchContext.Provider value={dispatch}>
{children}
</AppDispatchContext.Provider>
</AppStateContext.Provider>
);
};
export { AppProvider, useAppState, useAppDispatch, useApp };
// _app.js
import App from "next/app";
import React from "react";
import { AppProvider } from "../store";
class MyApp extends App {
componentDidMount() {
/***********************************/
// HERE I WOULD LIKE TO USE DISPATCH
/***********************************/
}
render() {
const { Component, router, pageProps } = this.props;
return (
<AppProvider>
<Component {...pageProps} />
</AppProvider>
);
}
}
export default MyApp;
If you really want to use hooks, then just put a wrapper around _app.js like this:
import React from 'react'
import App from 'next/app'
function MyComponent({ children }) {
// You can use hooks here
return <>{children}</>
}
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<MyComponent>
<Component {...pageProps} />
</MyComponent>
)
}
}
export default MyApp
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>
)
}