I am trying to understand and practice React hooks. I have the following code with me.
import React, { useState, useEffect, useReducer } from "react";
import "./App.css";
import DisplayUser from "./comp/DisplayUser";
const initialState = {
firstStep: 0,
};
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { firstStep: state.firstStep + action.payload };
case "decrement":
return { firstStep: state.firstStep - action.payload };
case "reset":
return initialState;
default:
return state;
}
};
export const UserContext = React.createContext();
function App() {
const [count, setCount] = useState(0);
const [tick, setTick] = useState(0);
const [step, dispatch] = useReducer(reducer, initialState);
const increaseCount = () => {
setCount((prevCount) => prevCount + 1);
};
const decreaseCount = () => {
console.log("De");
setCount((prevCount) => prevCount - 1);
};
useEffect(() => {
setTimeout(() => {
setTick((prevCount) => prevCount + 1);
}, 1000);
}, [tick]);
return (
<div className="App">
<section className="AppUseState">
<h2>useState</h2>
<article>
<h4>Count : {count}</h4>
<button onClick={() => increaseCount()}>Tick Plus</button>
<button onClick={() => decreaseCount()}>Tick Minus</button>
</article>
</section>
<section className="AppUseEffect">
<h2>useEffect</h2>
<article>
<h4>Tick : {tick}</h4>
</article>
</section>
<section className="AppUseReducer">
<h2>useReducer</h2>
<article>
<h4>Step : {step.firstStep}</h4>
<button onClick={() => dispatch({ type: "increment", payload: 5 })}>
Tick Plus
</button>
<button onClick={() => dispatch({ type: "decrement", payload: 3 })}>
Tick Minus
</button>
</article>
</section>
<section className="AppUseContext">
<h2>useContext</h2>
<article>
<UserContext.Provider value={{ userName: "Mohit" }}>
<DisplayUser />
</UserContext.Provider>
</article>
</section>
</div>
);
}
export default App;
Whenever a the setTick method in the setTimeout runs, its basically re rendering the DisplayUser component. (I have a console log in the DisplayUser component). My DisplayUser component looks like this
import React, { useContext } from "react";
import { UserContext } from "../App";
function DisplayUser() {
const user = useContext(UserContext);
console.log(user);
return (
<React.Fragment>
<p>Hello from {user.userName}</p>
</React.Fragment>
);
}
export default DisplayUser;
Can someone explain this behaviour to me as to why useEffect is basically re rendering my context api consumer component?
Related
I want to write context with UseReducer hook but an error
error this:dispatch is not a function
what is problem?
please help me guys
almost is correct but not working it.
I want to see the appropriate action by clicking on the buttons,
one is increment, one is decrement and the other is reset.
CounterOne
import { UseCount, UseCountActions } from "./CounterProvider";
const CounterOne = () => {
const count = UseCount();
const dispatch = UseCountActions();
return (
<div>
<h2>count is:{count}</h2>
<button onClick={() => dispatch({ type: "add", value: 1 })}>
Addone{" "}
</button>
<button onClick={() => dispatch({ type: "decrement", value: 1 })}>
decrement
</button>
<button onClick={() => dispatch({ type: "reset" })}>reset</button>
</div>
);
};
export default CounterOne;
CounterProvider
import React, { useReducer, useState } from "react";
import { useContext } from "react";
const CounterContext = React.createContext();
const CounterContextDispather = React.createContext();
const initialState = 0;
const reducer = (state, action) => {
switch (action.type) {
case "add":
return state + action.value;
case "decrement":
return state - action.value;
case "reset":
return initialState;
default:
return state;
}
};
const CounterProvider = ({ children }) => {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<CounterContext.Provider value={count}>
<CounterContextDispather.Provider value={dispatch}>
{children}
</CounterContextDispather.Provider>
</CounterContext.Provider>
);
};
export default CounterProvider;
export const UseCount = () => useContext(CounterContext);
export const UseCountActions = () => {
return CounterContextDispather;
};
export const UseCountActions = () => {
return useContext(CounterContextDispather);
};
There is a official example
I have React components :
Main.jsx
import { useState, useEffect } from "react";
import { Preloader } from "../Preloader";
import { Pokemons } from "../Pokemons";
import { LoadMore } from "../LoadMore";
function Main() {
const [pokemons, setPokemons] = useState([]);
const [loading, setLoading] = useState(true);
const [pokemonsPerPage] = useState("20");
const [pokemonOffset] = useState("0");
useEffect(function getPokemons() {
fetch(
`https://pokeapi.co/api/v2/pokemon?limit=${pokemonsPerPage}&offset=${pokemonOffset}`
)
.then((responce) => responce.json())
.then((data) => {
data.results && setPokemons(data.results);
setLoading(false);
});
}, []);
return (
<main className="container content">
{loading ? <Preloader /> : <Pokemons pokemons={pokemons} />}
<LoadMore />
</main>
);
}
export { Main };
LoadMore.jsx
import React from 'react'
function LoadMore() {
return (
<div className="button_container">
<a class="waves-effect waves-light btn-large" id="more">
More...
</a>
</div>
);
}
export { LoadMore };
I have created a button in the component. After clicking on it, the next 20 elements should be loaded. I created const [pokemonsPerPage] = useState("20"); and const [pokemonOffset] = useState("0"); in order to substitute these values into the request. The first is responsible for the number of objects on the page, the second is responsible for which element to start counting from. That is, now 20 elements are being output, starting from the very first one. (If you change const [pokemonOffset] = useState("0"); to 20, then the output will start from 21 elements). I'm sure that this is necessary for implementation, but I don't know what to do next
Help me complete this functionality
Here is what your Main.jsx should look like.
import { useState, useEffect } from "react";
import { Preloader } from "../Preloader";
import { Pokemons } from "../Pokemons";
import { LoadMore } from "../LoadMore";
function Main() {
const [pokemons, setPokemons] = useState([]);
const [loading, setLoading] = useState(true);
const [pokemonsPerPage] = useState(20);
const [page,setPage] = useState(1);
function getPokemons(pokemonOffset) {
fetch(
`https://pokeapi.co/api/v2/pokemon?limit=${pokemonsPerPage}&offset=${pokemonOffset}`
)
.then((responce) => responce.json())
.then((data) => {
data.results && setPokemons(data.results);
setLoading(false);
});
}
useEffect(() => {
const offset= page*pokemonsPerPage -pokemonsPerPage;
getPokemons(offset);
}, [page]);
return (
<main className="container content">
{loading ? <Preloader /> : <Pokemons pokemons={pokemons} />}
<LoadMore next={()=>setPage(p=>p+1)} />
</main>
);
}
export { Main };
while your Loadmore.jsx
import React from 'react'
function LoadMore(next) {
return (
<div className="button_container">
<a type="button" onclick={next} class="waves-effect waves-light btn-large" id="more">
More...
</a>
</div>
);
}
export { LoadMore };
I am getting error message like this toogleShow is not function whenever I am passing as props from child to parent component in react typescript functional component. In child, i am also declare an interface but still getting that error. I am not used to typescript with react but I am learning to use typescript with react.
App.tsx
import React, { useEffect, useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import { useGetRecipesMutation } from "./services/recipeApi";
import Card from "./component/Card";
function App() {
const [query, setQuery] = useState("");
const [health, setHealth] = useState("vegan");
const [modal, setModal] = useState(false);
const [recipe, setRecipe] = useState({});
const [getRecipes, { data, isLoading }] = useGetRecipesMutation();
useEffect(() => {
getFoodRecipes();
}, [query, health]);
const getFoodRecipes = async () => {
await getRecipes({ query, health });
};
const toggleShow = (recipe: any) => {
setModal(!modal);
setRecipe(recipe);
};
const showModal = (recipe: any) => {
if (modal) {
return (
<>
<MDBModal show={modal} setShow={modal}>
<MDBModalDialog>
<MDBModalContent>
{recipe.label}
</MDBModalContent>
</MDBModalDialog>
</MDBModal>
</>
);
}
};
return (
<>
<div className="App">
<MDBRow className="row-cols-1 row-cols-md-3 g-4">
{data?.hits?.map((item: any) => (
<Card toggleShow={toggleShow} recipe={item.recipe} />
))}
</MDBRow>
{modal && showModal(recipe)}
</div>
</>
);
}
export default App;
Card.tsx
import React from "react";
import {
MDBCol,
MDBCardGroup,
MDBCard,
MDBCardImage,
MDBCardBody,
MDBCardTitle,
} from "mdb-react-ui-kit";
interface PropsFunction {
toggleShow: (item: any) => void;
}
const Card = (recipe: any, { toggleShow }: PropsFunction) => {
const { recipe: item } = recipe;
return (
<>
<MDBCol>
<MDBCardGroup>
<MDBCard className="h-100 mt-2 d-sm-flex">
<MDBCardImage
src={item.image}
alt={item.label}
position="top"
style={{ cursor: "pointer" }}
onClick={() => toggleShow(item)}
/>
<MDBCardBody>
<MDBCardTitle>{item.label}</MDBCardTitle>
</MDBCardBody>
</MDBCard>
</MDBCardGroup>
</MDBCol>
</>
);
};
export default Card;
Try declaring your Card component like this:
const Card: React.FC<PropsFunction> = ({recipe, toggleShow}) => {
And change your interface to:
interface PropsFunction {
toggleShow: (item: any) => void;
recipe: any;
}
I am creating a Project where I need to get value of a component state value to another component.
How can I get the value?
Information
Both are functional component.
I need to send data from A (state) to B(state).
A state data are set from react dropdown menu.Which is a button.
I don't use Link or Route to A dropdown that's why I can't get it with useParams()
I need to set value in B component which will fetch data with passing language.
I have import all needed things & don't have any warnings & error.
Code of component A
Send value from this language state to B
const A = () => {
const [language, setLanguage] = useState('en');
return (
<Navbar expand="xl" bg='light' expand={false}>
<Container>
<DropdownButton id="dropdown-basic-button" title={<MdOutlineLanguage />}>
<Dropdown.Item as="button" onClick={() => setLanguage('en')}>English</Dropdown.Item>
<Dropdown.Item as="button" onClick={() => setLanguage('ar')}>العربية</Dropdown.Item>
<Dropdown.Item as="button" onClick={() => setLanguage('bn')}>বাংলা</Dropdown.Item>
</DropdownButton>
</Container>
</Navbar>
)
};
export default A
Code of Component B
I need to get here A state value & set it to B state. Then pass to useGetDataQuery & fetch data.
const B = () => {
let [language, setLanguage] = useState('en')
const { data } = useGetDataQuery({language })
return (
<>
</>
)
}
export default B
Redux Section
I'm using readux & #reduxjs/toolkit to store fetch data. Can I store my language data to here. Than how can get to from anywhere of my component.
react-rotuer-dom v6
export default configureStore({
reducer: {
[dataOne.reducerPath]: dataOne.reducer,
[data2.reducerPath]: dataTwo.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false
}).concat([dataOne.middleware, dataTwo.middleware]),
})
Maybe instead of using useState, you can use a global state by using useContext because I think your language will be use on several places as request body and of course to edit the state value, you can combine it with useReducer.
// UPDATE
working code: https://codesandbox.io/s/sleepy-monad-21wnc
MyContext
import { useReducer, useContext, createContext } from "react";
const initialValue = {
language: "id"
// other: "value",
};
const AppContext = createContext(initialValue);
const AppReducer = (state, action) => {
switch (action.type) {
case "CHANGE_LANGUAGE":
return {
...state,
...action.payload
};
default:
return state;
}
};
const MyContext = ({ children }) => {
const [state, dispatch] = useReducer(AppReducer, initialValue);
return (
<AppContext.Provider value={[state, dispatch]}>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
export default MyContext;
this is the context and reducer component later on use in App.js
App.js
import A from "./A";
import B from "./B";
import MyContext from "./MyContext";
import "./styles.css";
export default function App() {
return (
<MyContext>
<div className="App">
<A />
<B />
</div>
</MyContext>
);
}
A.js
import { useAppContext } from "./MyContext";
const A = () => {
const [globalState, dispatch] = useAppContext();
const onChange = (e) => {
dispatch({
type: "CHANGE_LANGUAGE",
payload: {
[e.target.name]: e.target.value
}
});
};
return (
<>
<p>Start Of Component A </p>
<input value={globalState.language} name="language" onChange={onChange} />
<p>End Of Component A </p>
</>
);
};
export default A;
B.js
import { useAppContext } from "./MyContext";
const B = () => {
const [globalState, dispatch] = useAppContext();
return (
<>
<p>Start Of Component B </p>
<h2>Language Val : {globalState.language}</h2>
<p>End Of Component B </p>
</>
);
};
export default B;
I am trying to get my child component to look for the global state. As of now, I can log out my global state, I can see that it has been updated, but my components are not updating with the new value. I am not getting any errors but also have had a hard time find a good solution. I have the full repo(very small with only 1 component) if it helps here: https://github.com/jaysnel/context-api-react
Is there any reason my ChangeColor.js file is not updating with the new state change?
UpdateColor.js
import React, { useReducer, useEffect } from 'react'
import { useAppContext } from '../public/context/context'
export default function UpdateColor() {
let { myGlobaData } = useAppContext();
const [state, dispatch] = useReducer(reducer, myGlobaData);
function reducer(state, action) {
let newState = state.slice();
console.log(state);
if(newState !== undefined) {
switch(action.type) {
case 'UPDATE_COLOR':
newState[0].color = 'green';
return newState;
default:
throw new Error();
}
}
}
return (
<div>
<button onClick={() => dispatch({type: 'UPDATE_COLOR'})}>Update Color</button>
</div>
)
}
ChangeColor.js
import React from 'react'
import { useAppContext } from '../public/context/context'
export default function ChangeColor() {
const { myGlobaData } = useAppContext();
console.log("ChangeColor.js", myGlobaData)
return (
<div>
<h2 style={{color: myGlobaData[0].color}}>I Change Colors Based On Global State.</h2>
</div>
)
}
context.js
import { createContext, useContext } from 'react';
const AppContext = createContext();
export function AppWrapper({ children }) {
const state = {
myGlobaData: [
{
color: 'red',
text: 'new text new me'
}
],
}
return (
<AppContext.Provider value={state}>
{children}
</AppContext.Provider>
);
}
export function useAppContext() {
return useContext(AppContext);
}
I guess, your issue where did you used the reducer. Component ChangeColor.js don't know what are you doing inside UpdateColor.js. The solution is if put the reducer into global context context.js and then you have to acsess your reducer globally.
I did created and pushed to github working example with two different approaches to using an actions reducer. working example
UpdateColor.js
import { useAppContext } from '../public/context/context'
export default function UpdateColor() {
const { dispatch } = useAppContext();
const withPayload = () => {
dispatch({
type: 'UPDATE_COLOR_WITH_PAYLOAD',
payload: {color: 'blue', text: 'new text from updateColor.js'}})
}
const intoReducer = () => {
dispatch({type: 'UPDATE_COLOR_INTO_REDUCER'})
}
return (
<div>
<button onClick={withPayload}>Update Color with payload</button>
<button onClick={intoReducer}>Update Color into reducer</button>
</div>
)
}
ChangeColor.js
import { useAppContext } from '../public/context/context'
export default function ChangeColor() {
const { state } = useAppContext();
return (
<div>
<h2 style={{color: state.color}}>I Change Colors Based On Global State.</h2>
<p style={{textAlign: 'center'}}>{state.text}</p>
</div>
)
}
context.js
import { createContext, useContext, useReducer } from 'react';
const AppContext = createContext();
export function useAppContext() {
return useContext(AppContext);
}
function reducer(state, action) {
switch (action.type) {
case 'UPDATE_COLOR_WITH_PAYLOAD':
return action.payload;
case 'UPDATE_COLOR_INTO_REDUCER':
action.color = 'green';
action.text = 'new text from reducer';
return action;
default:
return state;
}
}
export function AppWrapper({ children }) {
const initialState = { color: 'red', text: 'new text new me' };
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
);
}