React: how to fire useeffect using context variable as dependency? - reactjs

I was trying to figure out how to fire a useEffect when its dependency is a useContext variable. My Context has an "update" variable, which at one point changes, but the effect isn't fired.
import { useEffect, useState, useContext } from "react"
import context from "./Context"
const Layout = () => {
const ctx = useContext(context)
const [updateState, setUpdateState] = useState(ctx.update)
useEffect(() => {
console.log("effect fired")
}, [updateState])
return (
<div>
</div>
)
}
export default Layout
I tested whether the issue was my context "update" variable not changing, but it does. I will appreciate any help with this.

Your problem is that you used useState. This in effect memoized/froze the value of updateState, to the first run of this component.
You see useState takes a single arg (default value). Which is only used on the first render of the component, to populate updateState. When ctx.update changes, useState already has a default to set updateState to, so it is ignored.
Instead, you can remove useState completely...
import { useEffect, useState, useContext } from "react"
import context from "./Context"
const Layout = () => {
const { update as updateState } = useContext(context);
// Notice I have removed the useState.
useEffect(() => {
console.log("effect fired")
}, [updateState])
return (
<div>
</div>
)
}
export default Layout

Related

When I declare function in useEffect, it is not declared in html

I am implementing to change the state depending on it is hovered or not. But if I make it like the first code below, Too many re-renders. error will occur. In order to solve this problem, I used the useEffect to render only when the state changes, but an error called onHover is not defined occurs that the function declared in the user effect was not declared.
not using useEffect
import "./styles.css";
import { useState } from "react";
export default function App() {
const [isHover, setIsHover] = useState(false);
return (
<button onMouseEnter={setIsHover(true)} onMouseLeave={setIsHover(false)}>
click
</button>
);
}
using useEffect
import "./styles.css";
import { useEffect, useState } from "react";
export default function App() {
const [isHover, setIsHover] = useState(false);
useEffect(()=>{
const onHover = () => {
setIsHover(true)
}
},[isHover])
return (
<button onMouseEnter={onHover()} onMouseLeave={setIsHover(false)}>
click
</button>
);
}
What should I do to use the function declared in useEffect?
As you just want to setState so no need to use useEffect.
You can use without using useEffect as below.
import "./styles.css";
import { useState } from "react";
export default function App() {
const [isHover, setIsHover] = useState(false);
return (
<button onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
click
</button>
);
}
It has to do with scope. The onHover function is defined within the useEffect hook, so it goes out of scope once you're out of the hook's block. You'll have to define it directly inside the component, outside of any other block scope, and simply call it inside useEffect.
It will still result in onHover called so many times until the mouse leaves the element in question. To prevent it, you could add a condition like so:
const onHover = () => {
if (!isHover) {
setIsHover(true);
}
}

useRef's reference value change on re-render caused by strict mode while useState does not

I was trying to run something in useEffect, but did not wanted it to run on initial render so I created a hook useIsMounted.
import { useRef, useEffect } from 'react';
export const useIsMounted = () => {
const isMountedRef = useRef(false);
useEffect(() => {
console.log(isMountedRef.current);//prints false on first render and true on second render.
isMountedRef.current = true;
}, []);
console.log(isMountedRef.current);//prints false on both renders
return isMountedRef.current;
};
Here I have put two console logs and I am getting different values on two renders(one caused by strict mode) while the one outside useEffect returns same value on both renders.
Also whenever I use useState instead of useRef it console log's same value (false) on both render outside and inside of the useEffect.
here is the sample
import { useEffect, useState } from 'react';
export const useIsMounted = () => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
console.log(isMounted); //prints false on both renders
setIsMounted(true);
}, []);
console.log(isMounted);//prints false on both renders
return isMounted;
};

UseEffect but Only Update Certain Child in React Componet

Let's say I have a complex App component with several children. I'd like to update some of these children (ShouldUpdate) when my variable localStorageLayouts changes, so naturally I reach for useEffect(), and add localStorageLayouts as a dependency. The problem is that useEffect causes all children to update, and I only need my ShouldUpdate component to re-render. What are some work arounds to only render the components I need updated?
import React, { useEffect, useState } from "react";
import { useStoreState, useStoreActions } from "./hooks";
export default function App() {
const localStorageLayouts = useStoreState(
(state) => state.appData.localStorageLayouts
);
const [availableLayouts, setAvailableLayouts] = useState(localStorageLayouts);
useEffect(() => {
setAvailableLayouts(localStorageLayouts);
}, [localStorageLayouts]);
return (
//...
<div>
<VeryExpensiveToRender/>
<ShouldUpdate layouts = {availableLayouts}/>
<OkNotToUpdate/>
<ShouldUpdate layouts = {availableLayouts}/>
<OkNotToUpdate/>
</div>
)
}

how to set value in hooks

I have a problem with hooks in ReactJS
as you see here i defined a prop that should call from child component
but when i want to change the value by calling change component it doesn't work and my state doesn't set.
can someone help me?
don't forget to read the comments
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;
Use setState with a callback function
const setSelectedCollection = (e) => {
setCollection((state)=> {...state, e});
}
setCollection(e) - wont update the state immediately.
I want to Understand SetState and Prevstate in ReactJS
This might help you around, the useEffect will be called on each colletion update
import React, { useState, useEffect } from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
useEffect(() => {
console.log(collection)
}, [collection])
return (
<div>
<Collection onChoosed={(e) => setCollection(e)} />
</div>
)
}
export default SingleReminderPage;
it seems like the setCollection is called after the logging action to check something like that you can print the collection value on the component itself
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
{collection}
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;

Have to click twice to update state (useState hook) with onClick event

I've seen more questions being post here about this, but the solutions did not work for me.
The problem is that I: have to click twice to update the state of a constant defined using the useState hook
How can I make this work only having to click once?
Please see the code on CodeSandbox, or the code below:
App.js
import React, { useState } from "react";
import "./styles.css";
import Row from "./Row";
export default function App() {
const [adults, setAdults] = useState(1);
return <Row adults={adults} setAdults={setAdults} />;
}
Row.jsx
import React, { useEffect } from "react";
export default function({ adults, setAdults }) {
useEffect(() => {
console.log(adults);
}, [adults]);
return (
<>
<button onClick={() => setAdults(adults++)}>{adults}</button>
</>
);
}
Either do:
setAdults(adults+1)
or:
setAdults(++adults)
Be careful using ++ because someObject.counter++ will mutate someObject.

Resources