Memorize react change handler - javascript

I know that react component will render everytime state changes and useCallBack memorize function depending on the dependency array provided.
But If I have this:
<input type="text" name="name" value={values.name} onChange={handleChange} />
<input type="text" name="phone" value={values.phone} onChange={handleChange} />
Whats the difference between these two handleChange implementation?
const handleChange = e => {
setValues(values => ({ ...values, [e.target.name]: e.target.value }))
}
and
const handleChange = useCallback(e => {
setValues(values => ({ ...values, [e.target.name]: e.target.value }))
}), [])
Code snippet needed in comment
const Child = () => {
console.log("Child rerendered");
return <div>Hello World </div>;
};
export default function App() {
const [count, setCount] = useState(0);
console.log("Parent rerendered");
return (
<div className="App">
<Child />
<button
onClick={() => {
setCount((x) => x + 1);
}}
>
{count}
</button>
</div>
);
}

The difference is that with the second one using useCallback, you'll keep using the first handleChange function you create; with the first one where you don't use useCallback, you're creating a new handler function each time. (Technically, you create a new one every time in both cases, but useCallback will only return the first one that gets created, because you've provided an empty dependency array.) More in the documentation.
It doesn't matter much when giving the handler function to input or other native HTML elements, or even simple components. If you were providing this handler function to a complex component, it might be better to use useCallback if that complex component optimizes re-rendering (for instance, with React.memo, PureComponent, or shouldComponentUpdate).

Related

Parent's methods in array dependencies in useCallback

Let say we have parent and children components like this:
const {useState, useCallback} = React;
const ComponentB = (props) => {
const [text, setText] = useState('')
const { onClick } = props
const handleChange = useCallback((event) => {
setText(event.target.value)
}, [text])
const handleClick = useCallback(() => {
onClick(text)
}, [onClick, text]) // Should I to take into account 'onClick' props?
return (
<div>
<input type="text" onChange={ handleChange } />
<button type="button" onClick={ handleClick }>Save</button>
</div>
)
}
const ComponentA = () => {
const [stateA, setStateA] = useState('')
const handleSetStateA = useCallback((state) => {
setStateA(state)
}, [stateA])
return (
<div>
<ComponentB onClick={ handleSetStateA } />
{ stateA && `${ stateA } saved!` }
</div>
)
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<ComponentA />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
React documentation says that:
every value referenced inside the callback should also appear in the dependencies array
And I'm wondering if I need to put onClick method to array dependencies in useCallback? And if so, why I should do that?
And I'm wondering if I need to put onClick method to array dependencies in useCallback?
Yes.
And if so, why I should do that?
Because it's possible that your component will get re-rendered with a new and different function for the onClick prop that behaves differently from the old one. Without saying it's a dependency, you'll continue using the old value.
In fact, in your given code, it's not just possible but definite: you create a new handleSetStateA function every time stateA changes.
That said, in ComponentA:
There's no reason to have stateA as a dependency in your useCallback creating handleSetStateA; handleSetStateA never uses stateA. (It uses the state setter function for it, but that's not the same thing.)
There's not really any reason for handleSetStateA at all; just pass setStateA directly as onClick. But I'm assuming you do more than just setting the state in that function and just left things out for the purposes of the question.
(Similarly, in ComponentB there's no reason for text to be a dependency on the useCallback for handleChange; handleChange doesn't use text.)
But even if you change ComponentA to pass setStateA directly (or at least provide a stable function), ComponentB shouldn't rely on onClick being unchanging between renders, so you'd use onClick in your useCallback dependency list regardless.
Finally: There's not much point in using useCallback with functions you're passing to unmemoized components. For it to be useful, the component you're providing the callback function to should be memoized (for instance, via React.memo or, for a class component, via shouldComponentUpdate). See my answer here for details on that.
Here's an updated version of your snippet using React.memo and only the necessary dependencies; I've left handleSetStateA in (I added a console.log so it isn't just a wrapper):
const { useState, useCallback } = React;
const ComponentB = React.memo(({ onClick }) => {
const [text, setText] = useState("");
const handleChange = useCallback((event) => {
setText(event.target.value);
}, []);
const handleClick = useCallback(() => {
console.log(`Handling click when text = "${text}"`);
onClick(text);
}, [onClick, text]);
return (
<div>
<input type="text" onChange={handleChange} />
<button type="button" onClick={handleClick}>
Save
</button>
</div>
);
});
const ComponentA = () => {
const [stateA, setStateA] = useState("");
const handleSetStateA = useCallback((state) => {
console.log(`Setting stateA to "${state}"`);
setStateA(state);
}, []);
return (
<div>
<ComponentB onClick={handleSetStateA} />
{stateA && `${stateA} saved!`}
</div>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<ComponentA />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

How to pass two parameters to custom event handler in React?

I am using Ant design and they have a component called Switch, Switch have a custom event handler
import "./styles.css";
import "antd/dist/antd.css";
import { Switch } from "antd";
import { useState } from "react";
export default function App() {
const [status, setStatus] = useState(true);
const handleChange = (e) => {
// e is a boolean variable, true or false
console.log(e);
setStatus(e);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Switch checked={status} onChange={handleChange}></Switch>
</div>
);
}
and I have so much of question about that kind of syntax
Does
onChange={handleChange}
is equivalent with
onChange={(e) => handleChange(e)}
If for some reason, I can only write
onChange={handleChange}
how can I pass the real event handler to handleChange to do some stopPropagation, some thing likes
const handleChange = (e, reale) => {
reale.stopPropagation()
setStatus(e);
};
Simple codesandbox to understand what I said.
Does
onChange={handleChange}
is equivalent with
onChange={(e) => handleChange(e)}
They're equivalent if and only if the function receives one argument. But for ant design switches, the onChange actually passes in two parameters: the new value, and the event object. So if you want to create a new function and then call handleChange in that function, you'll probably want to pass both parameters through:
onChange={(value, e) => handleChange(value, e)}
You can write thing something like
onChange={(e) => handleChange(e, reale)}

Is there a performance/usability benefit to using useCallback for InputElement Events?

When not passing a callback to a child and just using it on the present component, Is there a benefit to wraping the callback in a useCallback?
This:
const Foo = (
const [count, setCount] = useState(500);
const onChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setCount(Number(e.target.value));
}, [setCount]);
return (
<>
<div>
Delay: <input value={count} onChange={onChange} type="number" />
</div>
</>
);
Verse This:
const Foo = (
const [count, setCount] = useState(500);
const onChange =(e: React.ChangeEvent<HTMLInputElement>) => {
setCount(Number(e.target.value));
};
return (
<>
<div>
Delay: <input value={count} onChange={onChange} type="number" />
</div>
</>
);
Codesandbox: https://codesandbox.io/s/loving-stonebraker-48xhs?file=/src/Counter.tsx
I see no benefit there when used internally since each state update (setCount(Number(e.target.value));) necessarily rerenders the component anyway and all the callback is doing is enqueueing a state update.
There may be a small (very negligible) improvement in memory usage if using a single declared, memoized function for the life of the component.
If the callback was used in an useEffect hook it could be provided as a stable reference and be removed from dependency arrays, but typically here you'd just move the function into the effect callback. This doesn't fit the use case you are asking about, but it's a valid use case that isn't passing callbacks down to children.

React how to re-render component only when the user clicks a button

I tried my best to search for a similar question before posting. I've got a Summary component in my project that accepts three user selected props (part #, start date, and end date), calls an API, then displays the fetched data. My problem is that the component re-renders every time the user changes one of the parameters (e.g. picks a new start date).
Ideally, the user would instead click an "Apply" button that would re-render the component using the set of three props. I tried using React.useRef() to create a reference to the component that I would use to update the Summary's state in a button's onClick event but no luck. I would greatly appreciate any advice on how to structure this situation. I'm editing the question to provide an extremely simple example below.
This is a sample application with an App.js and a Summary.jsx component. The code for App.js is as follows:
import React from "react";
import Summary from "./Components/Summary";
function App() {
const [input1, setInput1] = React.useState("");
const [input2, setInput2] = React.useState("");
const [input3, setInput3] = React.useState("");
return (
<>
<input
type="text"
id="input1"
onChange={(e) => setInput1(e.target.value)}
/>
<input
type="text"
id="input2"
onChange={(e) => setInput2(e.target.value)}
/>
<input
type="text"
id="input3"
onChange={(e) => setInput3(e.target.value)}
/>
<button
type="button"
onClick={() => {
alert("button has been clicked.");
}}
>
Apply
</button>
<Summary i1={input1} i2={input2} i3={input3} />
</>
);
}
export default App;
The code for Summary.jsx (contained in a Components folder) is as follows:
import React from "react";
const Summary = (props) => {
return (
<h1>{`Input 1: ${props.i1} Input 2: ${props.i2} Input 3:
${props.i3}`}</h1>
);
};
export default Summary;
You can see that as the user types into any of the inputs, it automatically re-renders the components as the state changes and thus the props that are supplied to the Summary component. Ideally, I would like no change to occur until the user hits the Apply button (I just supplied a bogus alert message as the onClick functionality for now).
If you don't want the Summary component to be re-rendered every time the parent component changes, I suggest using conditional rendering
Have a state isSubmited that defaults False, and set to True when user clicks Apply
Only render Summary when isSubmmited is True. If false, renders nothing (null)
If you want to switch isSummited back, pass a handler function setBack = () => setSubmited(false) as a props to the appropriate component
Something like this:
// App.js
function App() {
const [input1, setInput1] = React.useState("");
const [input2, setInput2] = React.useState("");
const [isSubmitted, setSubmitted] = React.useState(false);
return (
<>
<input
type="text"
id="input1"
value={input1}
onChange={(e) => setInput1(e.target.value)}
/>
<input
type="text"
id="input2"
value={input2}
onChange={(e) => setInput2(e.target.value)}
/>
<button
type="button"
onClick={() => {
alert("button has been clicked.");
setSubmitted(true);
}}
>
Apply
</button>
{/* Ternary operator */}
{isSumitted ? (
<Summary
i1={input1}
i2={input2}
afterDataFetch={() => setSubmitted(false)}
/>
) : null}
</>
);
}
// Summary.js
function Summary(props) {
const { i1, i2, i3, afterDataFetch } = props;
useEffect(() => {
fetchData();
// This will trigger 'setSubmitted(false)'
afterDataFetch();
});
}
Edit: As per request, to implement "keeping the old state and only send new state to Summary when click Submit", I have come up with a solution:
Besides the 3 input states, I also have a data state that is responsible for keeping the old states of the individual input fields (states from the previous Submit)
Therefore, the data state will only get updated when user clicks Submit
// App.js
function App() {
const initial = {
input1: "",
input2: "",
};
const [input1, setInput1] = useState("");
const [input2, setInput2] = useState("");
const [data, setData] = useState(initial);
return (
<>
<input
type="text"
id="input1"
value={input1}
onChange={(e) => setInput1(e.target.value)}
/>
<input
type="text"
id="input2"
value={input2}
onChange={(e) => setInput2(e.target.value)}
/>
<button
type="button"
onClick={() => {
setData({
input1: input1,
input2: input2,
});
}}
>
Apply
</button>
<Summary i1={data.input1} i2={data.input2} />
</>
);
}
// Summary.js
function Summary(props) {
const { i1, i2 } = props;
return <pre>{JSON.stringify({ i1, i2 })}</pre>;
}
export default React.memo(Summary);
Note the use of React.memo in the Summary.js file. This is for some rendering optimization. As you can imagine, the Summary component may get re-rendered (through setInput1 or setInput2), even though the data state has not changed. Therefore, using React.memo, per the docs:
If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
If your Summary component fetches API every time it re-renders, that could be a pretty good optimization (prevent refetching when data has not changed - although you could use solution like "useSWR" to cache response anyway)

How can I acess value from textfield in another module in reactJS

I want to have access to the value of a textField in another module in reactjs. The module that wants to have access does not import the whole textfield but only needs access to the value. What is the best way to access the value variable in another module in reactjs?
Here is my functional textField component:
export default function textField(props) {
const [value, setValue] = React.useState("");
const handleChange = (event) => {
setValue(value);
};
return (
<div>
<TextField
id="outlined-multiline-static"
label="Frage"
multiline
onClick={handleClickOpen}
rows={4}
value={value}
placeholder="hello"
variant="outlined"
style={{
backgroundColor: "white",
}}
/>
</div>
);
}
You can send onTextFieldChange function as props whenever textField's value changes you can pass a value to onTextFieldChange function and you can use it in the parent component.
There is an alternate way, Redux
You should try to use redux for the shared state between components which are either not related directly(i.e. sibling components or have a lengthy hierarchy). For small applications, redux is overkilled so should be avoided.
The most likely option that comes to mind here is the concept of lifting state, in which the nearest ancestor component has some means by which it also keeps track of the state, and the passes it into the sibling that needs to track it. You could make this an optional feature of your module by allowing a onChangeCallback prop that is called on each change; this prop could then be passed a setSharedState hook that would set the state on the ancestor:
const ParentComponent = () => {
const [textfieldVal, setTextfieldVal] = useState();
return (
<TextField onChangeCallback={setTextFieldVal} />
);
}
And you update your module to something like:
export default function textField(props) {
const [value, setValue] = React.useState("");
const {onChangeCallback} = props;
const handleChange = (event) => {
setValue(value);
if (typeof onChangeCallback === 'function') {
onChangeCallback(event); // or value, I'm not sure which you should be using here, this might be incorrect
}
};
return (
<div>
<TextField
id="outlined-multiline-static"
label="Frage"
multiline
onClick={handleClickOpen}
rows={4}
value={value}
placeholder="hello"
variant="outlined"
style={{
backgroundColor: "white",
}}
/>
</div>
);
}
This is just a rough example. Other options for passing around state freely would be using Redux or the Context API, but the former might be overkill for this one case and the latter probably not a great fit for a specific, single-use datapoint.
there are may option but the proper option in pass as props
const modle1= () => {
const [textfieldVal, setTextfieldVal] = useState();
return (
<TextField onChangeCallback={setTextFieldVal} />
);
}
export default function textField(props) {
const [value, setValue] = React.useState("");
const {onChangeCallback} = props;
const handleChange = (event) => {
setValue(value);
if (typeof onChangeCallback === 'function') {
onChangeCallback(event); // or value, I'm not sure which you should be using here, this might be incorrect
}
};
return (
<div>
<TextField
id="outlined-multiline-static"
label="Frage"
multiline
onClick={handleClickOpen}
rows={4}
value={value}
placeholder="hello"
variant="outlined"
style={{
backgroundColor: "white",
}}
/>
</div>
);
}
or you can use create context and use context and provide the parent to provide and the child as a consumer but the best option pass as a prop
I would recommend you use the parent-child nature of react in order to handle this. Since I'm not sure the relationship between the other module and this module, I'll provide a rough skeleton.
In a parent component:
export default function ParentComponent(){
const [status, updateStatus] = useState("")
return(
<TextView updateParent={updateStatus}>
</TextView>
)
}
Then in your child component:
const handleChange = (event) => {
setValue(value);
props.updateParent(value);
};
If they are siblings, I would use a parent component and then pass the state down to the sibling. Otherwise, as appropriate, use this parent child relationship to pass and update state.
HTH

Categories