I am relatively new to react and cant seem to get my global context to update.I have wrapped all my child components with the provider but it seems like my update methods in my useState() variables are not working as expected. In my index.js file, I wrap my App component in the provider first:
import App from './App';
import reportWebVitals from './reportWebVitals';
import {
BrowserRouter,
Route,
Routes
} from "react-router-dom";
import {UserContextProvider} from './components/UserContext';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<UserContextProvider>
<App/>
</UserContextProvider>
);
Here is my UserContext.js file:
import React from "react";
import { useState, createContext } from "react";
export const UserContext = createContext({})
export const UserContextProvider = ({children}) =>{
const [contextUsername, setUserName] = useState(null)
const [contextFirstname, setFirstName] = useState(null)
const [contextLastname, setLastName] = useState(null)
const [contextLoggedin, setLoggedIn] = useState(false)
const value={contextUsername, setUserName, contextFirstname, setFirstName,
contextLastname,setLastName,contextLoggedin,setLoggedIn}
return(
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
)
}
I am trying to access my context in my Landing component:
import React, { useContext } from 'react';
import { useState } from 'react';
import {Link, useNavigate} from "react-router-dom"
import { UserContext } from './UserContext';
const Landing = () =>{
const {contextUsername,setUserName} = useContext(UserContext)
const onExit = (convertedData) =>{
setUserName("test")
console.log(contextUsername)
navigate('/');
}
return(
<div>
<button onLick={onExit}></button>
</div>
)
}
export default Landing
However, in my console.log statement my contextUsername is 'undefined'.
The update state doesn't work like that. react create a queue for state update so we can not get the latest value right after the update call. since you are console.log(contextUsername) right after setUserName it console's undefined.
and don't pass updateState-method(ex. setUserName) directly in context create method to change state value from there call updateState-method(ex. setUserName) like
const UpdateUserName = (value) =>{
setUserName(value)
}
if you want to check the updated value of contextUsername use useEffect
useEffect(() => {
console.log(contextUsername);
},[contextUsername])
Related
I am learning Redux, in this app I am using react-redux and redux, I am not mutating the store's state, but still my app is not re-rendering
I have this basic counter app, you press the + button the number increases, you press the - button it decreases
My code:
app.js :
`
import './App.css';
import { useDispatch } from 'react-redux';
import { store } from './store';
function App() {
const dispatch = useDispatch();
const handleIncrease = () => {
console.log("+");
dispatch({
type : 'aumentar'
})
console.log(store.getState());
}
const handleDecrease = () => {
console.log("-");
dispatch({
type : 'restar'
})
}
return (
<div className="App">
<h1>Contador</h1>
<h3>{store.getState()}</h3>
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
</div>
);
}
export default App;
`
index.js :
`
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
`
store.js
`
import { legacy_createStore as createStore } from "redux";
let initialState = 0;
const reducer = (state = initialState, action) => {
switch(action.type){
case 'aumentar' :
return state + 1;
case 'restar' :
return state - 1;
default :
return state;
}
}
export const store = createStore(reducer);
`
You need to use useSelector to access entries from the state:
// 1. import the hook
import { useDispatch, useSelector } from 'react-redux';
import { store } from './store';
function App() {
const dispatch = useDispatch();
// 2. use it to extract what you need
const count = useSelector(state => state);
const handleIncrease = () => {
console.log("+");
dispatch({
type : 'aumentar'
})
}
const handleDecrease = () => {
console.log("-");
dispatch({
type : 'restar'
})
}
// 3. use the extracted variable inside JSX
return (
<div className="App">
<h1>Contador</h1>
<h3>{count}</h3>
<button onClick={handleIncrease}>+</button>
<button onClick={handleDecrease}>-</button>
</div>
);
}
When your state will become more complex / you will use more reducers, your code will look like:
const whatYouNeed = useSelector(state => state.reducerName.something);
I'm trying to make movies app for portfolio, I got an api with some data and I'm trying to fetch it with useEffect and then setting the state, I also use useContext hook for passing data to children props but the data is empty.
This is App.js
import "./App.css";
import { useEffect, useState, createContext } from "react";
import Axios from "axios";
import { Main } from "./components/Main/Main";
export const AppContext = createContext();
function App() {
const [data, setData] = useState([]);
useEffect(() => {
Axios.get("http://www.omdbapi.com/?s=star wars&apikey=459f1ce1").then((res) => {
setData(res.data.Search);
})
}, []);
return (
<div className="App">
<AppContext.Provider value={data}>
<Main />
</AppContext.Provider>
</div>
);
}
export default App;
This is Main.js
import React from "react";
import { useContext } from "react";
import { AppContext } from "../../App";
import "./Main.css";
export const Main = () => {
const { data } = useContext(AppContext);
console.log(data)
return <div>
</div>;
};
export default Main;
Currently the value of the context is just the value of the data state. To be able to destructure the context change your value to an object containing the data.
<AppContext.Provider value={{ data }}>
I'm trying to add AOS library to react. My code is like this now which is giving me an error that init is undefined:
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Menubar from './Components/Menubar/Menubar';
import Products from './Components/Products/Products';
import { useEffect, useState } from 'react';
import 'aos/dist/aos.css'
import { AOS } from 'aos';
function App() {
const [product, setProduct]= useState([]);
useEffect(()=>{
fetch('https://fakestoreapi.com/products')
.then(Response=>Response.json())
.then(data=>setProduct(data))
},[]);
AOS.init();
const [count, setCount]= useState(0);
return (
<div>
<Menubar count={count}></Menubar>
<Products count={count} setCount={setCount} product={product}></Products>
</div>
);
}
export default App;
And when i Try to use the AOS.init() with hook i get Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css';
import Menubar from './Components/Menubar/Menubar';
import Products from './Components/Products/Products';
import { useEffect, useState } from 'react';
import 'aos/dist/aos.css'
import { AOS } from 'aos';
function App() {
const [product, setProduct]= useState([]);
useEffect(()=>{
fetch('https://fakestoreapi.com/products')
.then(Response=>Response.json())
.then(data=>setProduct(data))
},[]);
useEffect(()=>{
AOS.init();
},[])
const [count, setCount]= useState(0);
return (
<div>
<Menubar count={count}></Menubar>
<Products count={count} setCount={setCount} product={product}></Products>
</div>
);
}
export default App;
Instead of :
import { AOS } from "aos";
Try this :
import AOS from "aos";
I am trying to make a React project which fetches data from an API, and I'm trying to use useReducer hook with useContext from an external file.
Folder Structure:
public
src (and in src folder, I have context.js and reducer.js along with rest of the components and and other files)
package.json
reducer.js:
const reducer = (currentState, action)=>{
switch(action.type){
case 'setBreedList':
// console.log('setBreedList ran')
return {...currentState, breedList: [...currentState.breedList, action.payload]}
// return currentState
// return {...currentState.breedList, action.payload}
default:
return currentState
}
}
export default reducer
context.js
import React, {useContext, useReducer} from 'react';
import reducer from './reducer';
const AppContext = React.createContext();
const initalState = {
breedList: [],
imageList: []
}
const AppProvider = ({children})=>{
const [state, dispatch] = useReducer(reducer, initalState);
const fetchAllBreeds = ()=>{
fetch('https://dog.ceo/api/breeds/list/all')
.then(res=>res.json())
.then(data=>dispatch({type: 'setBreedList', payload: data}))
}
return (
<AppContext.Provider value={{...state, fetchAllBreeds}}>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = ()=>{
return useContext(AppContext)
}
export {AppContext, AppProvider}
and this is index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {AppProvider} from './utils/context';
ReactDOM.render(
<React.StrictMode>
<AppProvider>
<App />
</AppProvider>
</React.StrictMode>,
document.getElementById('root')
);
and finally, this is Input.jsx, where I call the fetchAllBreeds function:
import React from 'react'
import {useGlobalContext} from '../utils/context'
const Input = () => {
const {state, fetchAllBreeds} = useGlobalContext();
fetchAllBreeds();
console.log('hello')
return (
<div>
<p>hello</p>
</div>
)
}
export default Input
Now, when I try to call the fetchAllBreeds function in the Input.jsx file above, it prints on the console, 'hello', endlessly. I can't figure out why this is happening and I have tried some different things, but the result is the same. Will be grateful to your help.
Input component is calling fetchAllBreeds function, that will update the context value which in turn will re-render Input component, hence fetchAllBreeds will re-run. Don't call any method inside the component body which will cause re-renders.
Use useEffect hook inside Input component. which will execute only once.
import React, {useEffect} from 'react'
import {useGlobalContext} from '../utils/context'
const Input = () => {
const {state, fetchAllBreeds} = useGlobalContext();
useEffect(()=>{
fetchAllBreeds();
}, [])
return (
<div>
<p>hello</p>
</div>
)
}
export default Input
I'm defining a variable in Page1 and would like to access it in Page2 and then when clicking back to Page1 retrieve the same variable
So far, the variable is set on Page1 but cannot be retrieved on Page2
index.js
import {createStore, applyMiddleware, combineReducers} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'
import {Provider} from 'react-redux'
import variableReducer from './reducers'
const store = createStore(
variableReducer,
composeWithDevTools(applyMiddleware(thunk))
)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
serviceWorker.unregister();
actions/index.js
export const SET_MY_VARIABLE = 'SET_MY_VARIABLE'
export const setMyVariable = myVariable => ({
type: SET_MY_VARIABLE,
payload: {myVariable}
})
reducers/index.js
import {SET_MY_VARIABLE} from '../actions'
const initialState = {
myVariable: ''
}
const variableReducer = (state=initialState, action) => {
switch (action.type) {
case SET_MY_VARIABLE:
return {
...state,
myVariable: action.payload.myVariable
}
default:
return state
}
}
export default variableReducer
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Setting variable<br />
Go to page 2
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)
components/Page2.js
import React from 'react'
import {connect} from 'react-redux'
const Page2 = (props) => {
const {myVariable} = props
console.log('props: ', props)
return (
<div>
Variable: {myVariable}
</div>
)
}
const mapState = state => {
console.log('map2 ', state.myVariable)
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page2)
I should be able to set variables to the store in one component and access them throughout the entire App. Instead, I'm not able to retrieve them
Instead of using action.payload.myVariable use action.payload in your reducer/index.js
I've discovered the answer to my problem. I needed to change the <a href tag to a <Link> from react-router-dom in the Page1 component. The <a href was causing a complete reload of all JS and losing state. Here's the corrected component:
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
import {Link} from 'react-router-dom'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Variable: {myVariable}<br />
<Link to="/page2">Go to page 2</Link>
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)