I have a filter on the drawer and want to reset state when the drawer is closed. There are filters by number of people, type of activity or input value.
This is the local state.
const [searchQuery, setSearchQuery] = useState<string>("");
const [count, setCount] = useState<number>(1);
const [checkedFilters, setCheckedFilters] = useState<string[]>([]);
This is the filter.
const activities = useMemo(() => {
return allActivities.filter(
(item) =>
item.participants >= count &&
item.activity.toLowerCase().includes(searchQuery)
);
}, [count, searchQuery, allActivities]);
To make the already given answers a bit more complete, if you want to reset all filters at once, I suggest to add a function as such:
function resetAllFilters() {
setSearchQuery("");
setCount(1);
setCheckedFilters([]);
}
You can now call resetAllFilters() when you want to reset all the filters, like so:
<button onClick={resetAllFilters}/>Clear</button>
And in your case, you can use the function in the same way as event that is called when the drawer closes.
To resetting state, you just need to set it back to empty or your predefined state:
setSearchQuery("");
AND/OR
setCheckedFilters([]);
AND/OR
setCount(1);
To know how to reset state, first you need to know what is the initial value for each and every state
const [searchQuery, setSearchQuery] = useState<string>(""); // initial value is ""
const [count, setCount] = useState<number>(1); // initial value is 1
const [checkedFilters, setCheckedFilters] = useState<string[]>([]); // initial value is []
After knowing the initial value, you just need to invoke setState with initial value as below
setSearchQuery("");
setCount(1);
setCheckedFilters([])
Related
I want to set purchase initial valua as increase useState value .like-
const [increase, setIncrease] = useState(purchase.minimum);
how can It possible?
`const [purchase, setPurchase] = useState({});
const [increase, setIncrease] = useState(0);`
Not sure if i understood what you meant but if your looking to change the value of increase each time the purchase changes you can try this using useEffect.
useEffect(() => {
setIncrease(Number(purchase.minimum));
}, [purchase])
const [purchase, setPurchase] = useState({});
const [increase, setIncrease] = useState(0);
Sometimes I have to use some native js libaray api, So I may have a component like this:
function App() {
const [state, setState] = useState(0)
useEffect(() => {
const container = document.querySelector('#container')
const h1 = document.createElement('h1')
h1.innerHTML = 'h1h1h1h1h1h1'
container.append(h1)
h1.onclick = () => {
console.log(state)
}
}, [])
return (
<div>
<button onClick={() => setState(state => state + 1)}>{state}</button>
<div id="container"></div>
</div>
)
}
Above is a simple example. I should init the lib after react is mounted, and bind some event handlers. And the problem is coming here: As the above shown, if I use useEffect() without state as the item in dependencies array, the value state in handler of onclick may never change. But if I add state to dependencies array, the effect function will execute every time once state changed. Above is a easy example, but the initialization of real library may be very expensive, so that way is out of the question.
Now I find 3 ways to reslove this, but none of them satisfy me.
Create a ref to keep state, and add a effect to change it current every time once state changed. (A extra variable and effect)
Like the first, but define a variable out of the function instead of a ref. (Some as the first)
Use class component. (Too many this)
So is there some resolutions that solve problems and makes code better?
I think you've summarised the options pretty well. There's only one option i'd like to add, which is that you could split your code up into one effect that initializes, and one effect that just changes the onclick. The initialization logic can run just once, and the onclick can run every render:
const [state, setState] = useState(0)
const h1Ref = useRef();
useEffect(() => {
const container = document.querySelector('#container')
const h1 = document.createElement('h1')
h1Ref.current = h1;
// Do expensive initialization logic here
}, [])
useEffect(() => {
// If you don't want to use a ref, you could also have the second effect query the dom to find the h1
h1ref.current.onClick = () => {
console.log(state);
}
}, [state]);
Also, you can simplify your option #1 a bit. You don't need to create a useEffect to change ref.current, you can just do that in the body of the component:
const [state, setState] = useState(0);
const ref = useRef();
ref.current = state;
useEffect(() => {
const container = document.querySelector('#container');
// ...
h1.onClick = () => {
console.log(ref.current);
}
}, []);
I need to detect every change of variable state BUT check code:
useEffect(() => {
if (value) {
setSelect(value);
}
}, [value]);
code above checkin every second and i got error:
Warning: Maximum update depth exceeded. This can happen when a
component calls setState inside useEffect, but useEffect either
doesn't have a dependency array, or one of the dependencies changes on
every render.
I need to check only when value is changed not every second!
value is props data from parent component.
const OrganisationUnitInput = ({
input
}) => {
const [showModal, setShowModal] = useState(false);
const value = input.value || [];
const value = input.value || [];
const handleChange = useCallback(
(value) => {
input.onChange(value);
setShowModal(false);
},
[input]
);
}
It is happening because you are setting value state and passing same value as dependency array . which results in re rendering
You misunderstood the useEffect hook, when you pass the second argument with some variable you are telling to react that the function of the effect must be executed if the variable change.
So in your code You are making an infinity loop.
To detect the change on the var only pass the var in the array and your function will be executed if any change occurred.
const Comp = () => {
const [value, setValue] = useState(0)
useEffect(() => {
// This code will be execute any time that the var value
// change
console.log("The value change, now is: ", value)
// so if you change the var here your will cause another call
// to this effect.
}, [value])
return <button onClick={() => { setValue(prev => prev + 1) }}>
Change Value
</button>
}
Every Time i try to refresh the page it returns to 0.
I'm taking the bestScore from turns when the match is equal to 6,
so basically everytime the matched cards hit 6 it will take the bestScore from the turns and save the bestScore to localStoarge and it works but when i try to refresh its gone
function App() {
const [cards, setCards] = useState([]);
const [turns, setTurns] = useState(0);
const [match, matchedCards] = useState(0);
const [bestScore, setBestScore] = useState(
localStorage.getItem("highestScoresss")
);
const [choiceOne, setChoiceOne] = useState(null); //Kullanici 1.karta basinca setChoiceOne o karti alacak ve guncelliyecek
const [choiceTwo, setChoiceTwo] = useState(null); //Kullanici 2.karta basinca setChoiceTwo o karti alacak ve guncelliyecek
const [disabled, setDisabled] = useState(false);
useEffect(() => {
if (match === 6) {
const highScore = Math.min(turns, bestScore);
setBestScore(highScore);
setBestScore(turns);
} else {
console.log("false");
}
}, [turns]);
useEffect(() => {
localStorage.setItem("highestScoresss", JSON.stringify(bestScore));
});
This Is the JSX
<div className="bilgi">
<p>Sıra: {turns}</p>
<p>Bulunan: {match}</p>
<p>En iyi Skor: {bestScore}</p>
<button onClick={shuffleCards}>Yeni Oyun</button>
</div>
</div>
The issue with your implementation is that you set state to 0 first, and then the useEffect hook runs and sets localStorage to the state value.
If you are potentially initializing your state to a value stored in localStorage then I suggest using a lazy initialization function so the initial state value is set before the initial render and eliminates the need for the additional useEffect hook to set state from storage. This reads from localStorage and returns the parsed value, or 0 if the parsed result is null or undefined.
const initializeState = () => {
return JSON.parse(localStorage.getItem("highestScoresss")) ?? 0;
};
...
const [bestScore, setBestScore] = useState(initializeState());
You will want to use a dependency array on the useEffect that is persisting the "highestScoresss" value in localStorage such that it only triggers when the bestScore state value updates and not on each and every render.
useEffect(() => {
localStorage.setItem("highestScoresss", JSON.stringify(bestScore));
}, [bestScore]);
After looking at the Code image, I think that you want that the bestScore to be set in the local storage with the key highestScores.
Your current useEffect hook implementation lacks a dependency array. You want that the localStorage should be updated every time a new bestScore is set.
For that add bestScore to the dependency array.
useEffect(() => /* YOUR OPERATION*/, [any_dependency])
Also, I recommend that you look at your first useEffect implementation again. You seem to be setting the bestScore state twice. Once with highScore and then with turns.
Recommended Reading
About Dependency Array - ReactJS Docs
I have an example like this:
codesandebox
I want to modify a state value in a callback, then use the new state value to modify another state.
export default function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("0");
const [added, setAdded] = useState(false);
const aNotWorkingHandler = useCallback(
e => {
console.log("clicked");
setCount(a => ++a);
setText(count.toString());
},
[count, setCount, setText]
);
const btnRef = useRef(null);
useEffect(() => {
if (!added && btnRef.current) {
btnRef.current.addEventListener("click", aNotWorkingHandler);
setAdded(true);
}
}, [added, aNotWorkingHandler]);
return <button ref={btnRef}> + 1 </button>
However, after this handler got called, count has been successfully increased, but text hasn't.
Can you guys help me to understand why this happened? and how to avoid it cleanly?
Thank you!
If count and state are always supposed to be in lockstep, just with one being a number and one being a string, then i think it's a mistake to have two state variables. Instead, just have one, and derive the other value from it:
const [count, setCount] = useState(0);
const text = "" + count;
const [added, setAdded] = useState(false);
const aNotWorkingHandler = useCallback(
e => {
setCount(a => ++a);
},
[]
);
In the above useCallback, i have an empty dependency array. This is because the only thing that's being used in the callback is setCount. React guarantees that state setters have stable references, so it's impossible for setCount to change, and thus no need to list it as a dependency.
There are few things causing the issue.
Setter does not update the count value immediately. Instead it "schedules" the component to re-render with the new count value returned from the useState hook. When the setText setter is called, the count is not updated yet, because the component didn't have chance to re-render in the mean time. It will happen some time after the handler is finished.
setCount(a => ++a); // <-- this updates the count after re-render
setText(count.toString()); // <-- count is not incremented here yet
You are calling addEventListener only once and it remembers the first value of count. It is good you have aNotWorkingHandler in the dependencies - the onEffect is being re-run when new count and thus new handler function comes. But your added flag prevents the addEventListener from being called then. The button stores only the first version of the handler function. The one with count === 0 closured in it.
useEffect(() => {
if (!added && btnRef.current) { // <-- this prevents the event handler from being updated
btnRef.current.addEventListener("click", aNotWorkingHandler); // <-- this is called only once with the first instance of aNotWorkingHandler
setAdded(true);
} else {
console.log("New event handler arrived, but we ignored it.");
}
}, [added, aNotWorkingHandler]); // <-- this correctly causes the effect to re-run when the callback changes
Just removing the added flag would, of course, cause all the handlers to pile up. Instead, just use onClick which correctly adds and removes the event handler for you.
<button onClick={aNotWorkingHandler} />
In order to update a value based on another value, I'd probably use something like this (but it smells of infinite loop to me):
useEffect(
() => {
setText(count.toString());
},
[count]
);
Or compute the value first, then update the states:
const aNotWorkingHandler = useCallback(
(e) => {
const newCount = count + 1;
setCount(newCount);
setText(newCount.toString());
},
[count]
);
I agree with #nicholas-tower, that if the other value does not need to be explicitly set and is always computed from the first one, it should be just computed as the component re-renders. I think his answer is correct, hope this context answers it for other people getting here.