Saving count variable when closing Dialog in React - javascript

I am using useState inside Dialog in React Material UI.
and I use useCallBack Function like this.
const [count,setCount] = React.useState({
count: 0
})
const countCallback = useCallback(
() =>
setCount({
...count,
count : count.count + 1
}),[setCount,count])
It shows incremented number when I click increment Button to execute this Callback in ChildComponent.
But when I closing Dialog and open Dialog again, count reset to 0.
Is there any way I can memorize count inside dialog?
I don't want to use Redux.

You can use localstorage for it and use it to get the initial value of count at component mounting using useEffect.
useEffect(() => {
const initialCount = localstorage.getItem("count"); //given that u name the localstorage variable as count when setting item //
if(initialCount){
setCount({count: initialCount})
}
},[])
You can use another useEffect to set the value of count or you can do it just before setting the state.

If you want to persist the code you have used you can set the state in parent component, send that state as a props to child component and return the new value from the child component back to parent component.
Following link will help you to pass data from child to parent component in react:
https://stackoverflow.com/a/44467773/8057264
Using this approach you can maintain the lastest count in your application.

Issue is, when you close the dialog, Dialog component unmounts due to which on opening the component again value of count sets to 0.
What you can do is, Send the value to parent component everytime it is changed. In parent component store the value in state/local storage and pass the value as a prop to dialog component.

Related

Cannot access React props in state

I'm passing some data from parent to children like the following
<Children title={title}/>
Inside the children I have a state like this :
const [state,setState] = useState(title ? title : '')
Problem is, when accessing the title directly like this {title} it's working, accessing state on the other hand does not work. Should I useEffect for this to get data from parent when the state is loaded ?
You need to use useEffect hook here. because useState is basically used to initialize the value and not to update.
useEffect(() => {
setState(title);
}, [title]);
Because the problem with your approach is that when you do -
const [state,setState] = useState(title ? title : '')
This will set your state variable to ''(empty string) because on the initial render of your child component there is no guarantee that you are going to get the value of title.
And when you get the value of title in your props. useState will not detect it.
So therefore to detect a change in your props and to setState based on updated props its recommended to use useEffect.
Access the props in the children component like this -
function childComponent(props) {
//props.title -- access it in your component.
}
But what you're trying in your code is not recommended, you can't mutate the state of props. Read React docs
This is the correct implementation of this;
<ClassA title = "class a"/>
function ClassA(props){
// access the title by calling props.title
}

Updating local and parent state causes unmounted component error

I have a table with buttons. I want to track the number of times each individual button is clicked along with the total number of times any button has been clicked. I can only get one-or-the-other to work.
I attempt to accomplish this by having a "global" state hook that is passed into each row of the table used for tracking total clicks. I also have a "local" state hook that is part of each component that makes up a row in the table for tracking individual clicks. I pass the "global" state from the parent to the children rows as a pair of callbacks (one for the getter, one for the setter).
Error Message:
Warning: 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.
in ButtonCell (created by clickableButtonCell)
in clickableButtonCell (created by Table)
Problematic Code:
export default function ButtonCell({
totalButtonClicks,
setTotalButtonClicks
}) {
// Having this local state variable causes the unmounted component error.
// Removing this state and its references will cause the error to go away.
// Or, removing the state variable passed into this component will cause
// the error to go away and the individualClicks state will work as intended.
const [individualClicks, setIndividualClicks] = useState(0);
const onClick = async () => {
await axios.get("www.github.com").then(() => {
setTotalButtonClicks(totalButtonClicks + 1);
setIndividualClicks(individualClicks + 1);
return null;
});
};
return (
<button type="button" onClick={onClick}>
Click Me! {individualClicks}
</button>
);
}
Code Sandbox: https://codesandbox.io/s/cranky-sun-wflot?file=/src/ButtonCell.js
Edit: https://codesandbox.io/s/hopeful-wing-7cwio?file=/src/ButtonCell.js
useMemo in the MyTable.js was causing rerender because of the props change (caused by setting state).

Purpose of React state

This might be a really stupid question but I am writing my first project in React and am struggling to understand the purpose of setState hooks.
As far as I understand, the setState hook is used to set current values used in a component that is scoped to that component only, and does not persist if a page is reloaded for example, the value is simply held in memory until it is destroyed.
My question is, what is the difference between using setState() to store values and just simply declaring a let variable and updating it the regular way? Both methods just seem to be holding a non-persisting value scoped to that component. What is the difference?
changes in the state automatically cause your app to re-render (in most cases), so typically you store data in a state that is being displayed and possibly changed throughout the app (a menu whose options can change based on previous selections, for example).
TL;DR, even though the answer's not very long:
setState or useState is the key for React to subscribe to your component's state and update your components when necessary. Using let variables for storing app state means React will never get to know about state change and won't rerender and update your components.
A short overview of React
The core principle of React is that your components, and consequentially your UI, are a function of your app's state. When your app's state changes, components "react" to this state change and get updated. Here's a simple example:
const CounterButton = () => {
// Create a state variable for counting number of clicks
const [count, setCount] = React.useState(0);
// Decide what the component looks like, as a function of this state
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
};
ReactDOM.render(<CounterButton />, document.querySelector('#root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
This is a component that just creates a button that shows how many times it has been clicked. Now, the component needs to store information about how many times it has been clicked - this means that the clicks count is a part of this component's "state". That's what we get from React.useState(0) - a state variable whose initial value is 0, and a function that allows us to change the value. Whenever you call setCount with some value, React gets to know that the CounterButton component's state has changed and thus the CounterButton component needs a rerender.
So in other words, React allows you to neatly and concisely define what internal state a component requires and what the component looks like as a function of this internal state (and external props). React does rest of the work - it manages the app's state, and whenever a piece of state changes anywhere in the app, React updates the components that depend on that. In other words, components "react" to data change.
To your question
If you use a simple let variable instead of React.useState in the above example, React will no longer get to know if the count variable has changed. useState is the key for React to "subscribe" to your component's state.
const CounterButton = () => {
// React will never get to know about this variable
let count = 0;
return (
<button onClick={() => count++}>
Count: {count}
</button>
);
};
In fact, for a functional component, let variables won't work in any case because while rendering a functional component, React internally runs the function. That would mean your let variable would be reset to its default value. The above reason is more relevant to class components. Using let variables to store component state is like hiding things from React - and that's not good because then there's no one else to rerender your component when component state changes :)
This part of the React docs is a bit relevant - it does not go into any details, though.
React re-renders the new / updated state on an event occurance storing value into a variable wont trigger re-render and data is passed on form parent component to child component through props and a change in state can be reflected among all the parts.
For example we need to print 100 elements on a page if an element is modified or updated in any way this triggers re-render but using var if the variable is modified or updated this won't cause re-render where in data wont be updated.

Filter state doesn't work correctly in React

My Parent component retrieves the checked inputs value from its child component.
So I have a function (handleChecked) that takes two parameters : one 'id' and one checked status 'isChecked' :
const handleChecked = useCallback(({ id, isChecked }) => {
const newCheckedValues = checkedValues.filter(current => current !== id);
setCheckedValues(newCheckedValues);
if (isChecked) {
newCheckedValues.push(id);
setCheckedValues(newCheckedValues);
}
}, [checkedValues]);
What my function is supposed to do :
1 - Get id and checked status from the clicked input,
2 - Check the state for duplicate id's,
3 - If present, remove it,
4 - Save the state,
5 - If checked, store the id in a temporary array,
6 - Save the new state.
What it does :
Well, all the task above except adding the value to the new state.
When I click a new input, the parent "checkedValues" state is empty and starts over from nothing.
Meaning that the temporary array created as the result of the filtered state, is also empty.
Right now, this function just adds an id in the state, then replace it by the new clicked input.
And I need to gather all the checked values and store them in the state before sending that to an api later. I manage to present my "expectations" in this sandbox :
https://codesandbox.io/s/react-hooks-filter-state-array-toggle-input-checked-gz504
It works, the only difference is it's in the same component and uses html form event and not props legacy.
Finally, I deconstructed my component in 3 :
A Parent component with the checkedValues state which calls a "ListItem" child, passing datas and states as props ;
A ListItem component which contains the handleCheck function and map the datas to display, passing the infos and the function to each mapped child.
An Item component which displays an input with its datas and the handleCheck function + deals with its own isChecked state to modify CSS dynamically according to its status.
This answer is of course customized to my own purposes and needs. It's not the most general but it answers my question and can maybe help someone. Thanks again #ChrisG for your help

Component not updating when I change the props that I pass to it in React

I have a functional component which has a child component. The child component displays some text which is passed onto it from the parent component via props. When I change the text in the parent component and pass it down, the child component still holds the old text.
Below is the an minimal reproducible example of the parent component, MainPage.
function MainPage(){
let text = "This is the original text";
setTimeout(function(){ text = "This is the new text" }, 3000);
return(<DisplayText text={text} />);
}
Below is DisplayText.
function DisplayText(props){
return(<p>{props.text}</p>)
}
How do I update the child component so that it displays 'This is the new text' instead of 'This is the original text' after 3 seconds?
Thanks in advance!
A component only updates once either its state changes or its props change. A state is a variable or set of variables which is remembered when the component re-renders. All other variables will go back to their default value as soon as the component re-renders. You can see it as the component's memory.
So in your case changing your text variable won't update your parent's state, and thus won't re-render the component, which in return won't re-render and update the child's component.
If you want your parent component to update it's state (and update the child's props) you need to declare your text variable like this:
const [text, setText] = React.useState("This is the original text");
Text is your variable, it is now included within your component's state and will be remembered when the component re-renders. You can give this any name you want.
setText is a function which updates your text variable and also re-renders your component and it's children. You can give this any name you want.
"This is the original text" is your initial state, the initial value for your text variable.
To update your state you can do something like this:
setText("This is the new text");
So in your case it will look something like this:
function MainPage(){
const [text, setText] = React.useState("This is the original text");
React.useEffect(() => {
const timeout = setTimeout(function(){
setText("This is the new text")
}, 3000);
return clearTimeout(timeout)
}, []);
return(<DisplayText text={text} />);
}
useEffect is necessary to be able to define your setTimeout as soon as the component mounts. It can be use to execute some code as soon as a certain variable (defined between the [] brackets) updates. For example: If you wrote it like this:
React.useEffect(() => {
// execute some code
}, [text])
It would execute some code as soon as your text variables changes. Leave the [] brackets empty to only trigger useEffect when the component mounts and unmounts.
Within the useEffect hook you declare your setTimeout, this sets your timer as soon as the component mounts in this case.
The return method within your useEffect clears your timeout again as soon as the component unmounts. This will prevent your timer from running indefinitely after your component unmounts.
In order for the variable to add a listener for the prop in a child component, it should be part of the state of the parent component, and the way you wrote your parent component is wrong, as you defined a static variable using let.
You have two choices, either follow the react hooks example posted by Luze (which is missing the useEffect function that will trigger the setTimeout after the component loads), or transform your parent component to a class, as follows:
import React from 'react';
class MainPage extends React.Component {
state={
text: "This is the original text",
};
componentDidMount(){
setTimeout(()=> { this.setState({
text: "This is the new text"
})
}, 3000);
}
render(){
return(<DisplayText text={text} />);
}
}

Categories

Resources