How to make a generic callback function react - javascript

I am creating a callback function for passing setState up from child to parent. However, I have found myself creating tonnes of functions for each type of state im using and was wondering if there would be a way to make this generic. Here is an example of one of the functions:
const setIsModalVisible = useCallback(val => {
setModalVisible(val);
}, [setModalVisible]);
Each function is the same but using a different setState. Any help would be great! Thanks

Create a function which returns callback
const GetCallback = (func) => {
return useCallback(
(val) => {
func(val);
},
[func]
);
};
and call that function by passing the setState as a argument.
CodeSandbox Link - https://codesandbox.io/s/quiet-lake-h2pzr?file=/src/App.js

React hooks (so useCallback too) must be used in root of the component function. Therefore You cannot make some generator for creating multiple useCalback.

Related

Difference between ComponentDidUpdate and SetState callback function

I've done some research about an error I got in my react native app
Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback..
I've done research but I still can't understand the difference between them. My theoretical idea is to remove an element from an array of objects, but when I use setState with a callback function I get the error. Can anyone point me in the right direction or show me an example of how I can use componentDidUpdate. The code example below works for now but I feel it could be improved on, and I believe that is why I'm getting my error
Delete_Task = (e) => {
this.setState(prevState => {
const tasks = prevState.daily.filter(task => task.ID !== e);
this.setState({daily: tasks});
})
}
You need to return the new state object instead of calling another setState inside the outer setState callback
change
this.setState({daily: tasks});
To
return {daily: tasks};
// OR
return {...prevState, daily:tasks}

Is logical to use React useCallback for this example?

I'm new in React hooks, in one of parts in our projects, we have a function to handle modal display in cards:
const toggleModal = () => setModalDisplay(!modalDisplay)
in this case, I have to pass this function to each child components... maybe for 10 or 1000 children!
So, regard to React JS DOC useCallback returns a memoized function to reduce the memory usage and handle expensive computing. but I have to pass params to second argument to refresh callback and return the new one.
but in this case, we have a state to handle modal toggle, that every times has changes between true and false. so, with considering the logic of useCallback and our use case, if I use useCallback for this example, practically that function (toggleModal) updates every times and useCallback is useless for this use case.
for example:
const memoizedModalToggle = useCallback(
() => {
setModalDisplay(!modalDisplay)
},
[modalDisplay],
);
finally, is this a correct using of useCallback? or I have mistake in implementation and logic?
Thanks
I think you are confused between set the states and update callback functions in a memoized function with useCallback
For example, you can memoized your callback function with an empty array of dependencies:
const memoizedModalToggle = useCallback(
() => {
setModalDisplay(!modalDisplay)
},
[]
);
it will be something like useEffect logic for callback functions.
Also you can use Set() object to test this flow, because Set is able to store unique functions by references and you are able to use set.size() for checking the number of callbacks have generated.
ok, use useCallback when you want to avoid having to update a component or function when the state changes
example:
{useCallback(<TableStore
params={[JSON.stringify([selectedRoute]), data]}
tableName="custom_table"
method="Select"
>
<ReactTable
allowExport
onRowClick={onRowClick}
columns={columns}
allowSelection={false}
/>
</TableStore>, [selectedRoute, data])}
Here the table will change and update only when we click on the row or when our data has been updated.
in your case, not need to use useCallback

How to works setState function?

I want to create a basic crud app. So i want to create a basic live search. For this reason, i have written a simple code. Like this
searchProduct = (e) => {
const query = e.target.value.toLowerCase();
const data = [...this.state.exampleProducts];
const filteredData = data.filter(p => {
return p.name.toLowerCase().includes(query);
})
console.log(filteredData);
}
When I use the setState function, the console.log output gives different results.
guessing you are new in Reactjs, please check this one first https://css-tricks.com/understanding-react-setstate/
Do not depend on this.state immediately after calling setState() and make use of the updater function instead.
setState has a callback function that you can use, but DO NOT use setState inside of it.
When I need to log something inside the state, prefer to use console.log in the render function in this way you won't miss anything.
The setState dose doesn't apply the change immediately. You can pass callback function like this:
this.setState({value: 'updated-value'}, () => {
console.log(this.state.value); // updated-value
// You can use your updated state here
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Using () => vs this.something in jsx

I am trying to comprehend when do we use something like in jsx of our react
something = {this._onRefresh}
something = {() => this._onRefresh}
something = {this._onRefresh()}
Where something could be something we are passing to our
child component
onChange Event
onClick event
Any other situation you can think about
Where our ._onRefresh() could be
._onRefresh = () => {
//changes state of our react component
//Call redux action
//call other functions
}
or In case of forms, it takes in event which triggered it
._onRefresh = (event) => {
//takes target value to search inside the data
//Store data in the backend
}
Can someone please explain me when do we need to use which one? will help me a lot in clearing my fundamentals of react and javascript
The points 1/2 and 3 are actually totally different.
Your point 3 executes a function and assigns the returning value to the something.
Your examples 1/2 assign a function to something
The case 3 could be used when for example you have the disable attribute, and you want to assign the returning true/false of a function.
The points 1 and 2 are assigning a function to the attribute, like for example with onClick attribute, which accepts a callback to be called on click.
The difference between the first and the second point is that if you put that code inside the render method, the second point creates a function for every render, which is not the best for performances.
Using the first point, you should pay attention on how you define the method for the reference of this inside the method.
If you define the class method as:
myMethod() {
console.log(this); // it will be undefined by default
}
Then you need to bind the this inside the constructor if you want to access this inside the method:
this.myMethod = this.myMethod.bind(this);
If you define the method as arrow function, it will keep the value of the this inside the method, so no need of the bind:
myMethod = () => {
console.log(this);
};
1- you are passing a function as a property to this component
2- you are creating a new function and passing it as a property to this component
3- you are passing the result (output) of calling _onRefresh as a property to this component
Option 1 is valid if _onRefresh is actual callback function that should be passed via a prop:
_onRefresh = () => console.log('refresh');
...
<Component onRefresh={this._onRefresh}/>
Where Component uses onRefresh like:
// do refresh
props.onRefresh();
Option 2 is valid if _onRefresh is actual callback function, and onRefresh prop is expected to be higher-order function by a component that accepts a callback:
_onRefresh = () => () => console.log('refresh');
...
<Component onRefresh={() => this._onRefresh}/>
Where Component handles onRefresh like:
// do refresh
const refreshFn = props.onRefresh();
refreshFn();
Option 3 is valid if _onRefresh is higher-order function that returns another function, and this is expected by a component that accepts a callback:
_onRefresh = () => () => console.log('refresh');
...
<Component onRefresh={this._onRefresh()}/>
Where Component handles onRefresh like:
// do refresh
const refreshFn = props.onRefresh();
refreshFn();
Scenarios in options 2 and 3 are much less probable because they don't have much uses in this particular case in React.
In this case option 1 is likely correct because _onRefresh does not return another function and is not expected by child component. Option 2 is a mistake that will result in _onRefresh being never called. Option 3 is a mistake that will result in _onRefresh being called instantly and undefined is not a function error later or, even worse, erroneous behaviour with no error.

What does useCallback/useMemo do in React?

As said in docs, useCallback
Returns a memoized callback.
Pass an inline callback and an array of inputs. useCallback will return a memoized version of the callback that only changes if one of the inputs has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
But how does it work and where is the best to use it in React?
P.S. I think visualisation with codepen example will help everyone to understand it better. Explained in docs.
This is best used when you want to prevent unnecessary re-renders for better performance.
Compare these two ways of passing callbacks to child components taken from React Docs:
1. Arrow Function in Render
class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <Button onClick={() => this.handleClick()}>Click Me</Button>;
}
}
2. Bind in Constructor (ES2015)
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <Button onClick={this.handleClick}>Click Me</Button>;
}
}
Assuming <Button> is implemented as a PureComponent, the first way will cause <Button> to re-render every time <Foo> re-renders because a new function is created in every render() call. In the second way, the handleClick method is only created once in <Foo>'s constructor and reused across renders.
If we translate both approaches to functional components using hooks, these are the equivalents (sort of):
1. Arrow Function in Render -> Un-memoized callback
function Foo() {
const handleClick = () => {
console.log('Click happened');
}
return <Button onClick={handleClick}>Click Me</Button>;
}
2. Bind in Constructor (ES2015) -> Memoized callbacks
function Foo() {
const memoizedHandleClick = useCallback(
() => console.log('Click happened'), [],
); // Tells React to memoize regardless of arguments.
return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}
The first way creates callbacks on every call of the functional component but in the second way, React memoizes the callback function for you and the callback is not created multiple times.
Hence in the first case if Button is implemented using React.memo it will always re render (unless you have some custom comparison function) because the onClick prop is different each time, in the second case, it won't.
In most cases, it's fine to do the first way. As the React docs state:
Is it OK to use arrow functions in render methods? Generally speaking,
yes, it is OK, and it is often the easiest way to pass parameters to
callback functions.
If you do have performance issues, by all means, optimize!
useCallback and useMemo are an attempt to bypass weak spots that come with the functional programming approach chosen with React hooks. In Javascript, each entity, no matter if it is a function, variable, or whatever, is created into the memory when the execution will enter the function's code block. This is a big issue for a React that will try to detect if the component needs to be rendered. The need for rerendering is deducted based on input props and contexts. Let's see a simple example without useCallback.
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = () => {
setCounter(counter + 1);
}
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
Note that the handleClick -function instance will be created on each function call inside the block, so the event handler's address on each call will be different. The React framework will always see the event handler as changed because of this. In the example above, React will think handleClick as a new value on each call. It simply has no tools to identify it as the same call.
What useCallback does, it internally stores the first introduced version of the function and returns it to the caller, if the listed variables have not changed.
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = useCallback(() => {
setCounter(counter + 1);
}, [])
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
Now, with the code above, React will identify the handleClick -event handler as the same, thanks to useCallback -function call. It will always return the same instance of function and React component rendering mechanism will be happy.
Storing the function internally by the useCallback will end up with a new problem. The stored instance of the function call will not have direct access to the variables of the current function call. Instead, it will see variables introduced in the initial closure call where the stored function was created. So the call will not work for updated variables. Thats why you need need tell if some used variables have changed. So that the useCallback will store the current function call instance as a new stored instance. The list of variables as the second argument of the useCallback is listing variables for this functionality. In our example, we need to tell to useCallback -function that we need to have a fresh version of counter -variable on each call. If we will not do that, the counter value after the call will be always 1, which comes from the original value 0 plus 1.
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = useCallback(() => {
setCounter(counter + 1);
}, [counter])
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
Now we have a working version of the code that will not rerender on every call.
It is good to notice that the useState -call is here just for the same reason. Function block does not have an internal state, so hooks are using useState, useCallback and useMemo to mimic the basic functionality of classes. In this sense, functional programming is a big step back in history closer to procedural programming.
useMemo is the same kind of mechanism as useCallback but for other objects and variables. With it, you can limit the need for component rerender, as the useMemo -function will return the same values on each function call if the listed fields have not changed.
This part of the new React hooks -approach is definitely the weakest spot of the system. useCallback is pretty much counterintuitive and really error-prone. With useCallback-calls and dependencies, it is too easy to end up chasing internal loops. This caveat we did not have with the React Class approach.
The original approach with classes was more efficient after all. The useCallback will reduce the need to rerender, but it regenerates the function again every time when some of its dependant variables will change, and matching if the variables have changes itself will make overhead. This may cause more rerenders than necessary. This is not the case with React classes.
I've made a small example to help others understand better how it behaves. You can run the demo here or read the code bellow:
import React, { useState, useCallback, useMemo } from 'react';
import { render } from 'react-dom';
const App = () => {
const [state, changeState] = useState({});
const memoizedValue = useMemo(() => Math.random(), []);
const memoizedCallback = useCallback(() => console.log(memoizedValue), []);
const unMemoizedCallback = () => console.log(memoizedValue);
const {prevMemoizedCallback, prevUnMemoizedCallback} = state;
return (
<>
<p>Memoized value: {memoizedValue}</p>
<p>New update {Math.random()}</p>
<p>is prevMemoizedCallback === to memoizedCallback: { String(prevMemoizedCallback === memoizedCallback)}</p>
<p>is prevUnMemoizedCallback === to unMemoizedCallback: { String(prevUnMemoizedCallback === unMemoizedCallback) }</p>
<p><button onClick={memoizedCallback}>memoizedCallback</button></p>
<p><button onClick={unMemoizedCallback}>unMemoizedCallback</button></p>
<p><button onClick={() => changeState({ prevMemoizedCallback: memoizedCallback, prevUnMemoizedCallback: unMemoizedCallback })}>update State</button></p>
</>
);
};
render(<App />, document.getElementById('root'));
An event handler gets recreated and assigned a different address on every render by default, resulting in a changed ‘props’ object. Below, button 2 is not repeatedly rendered as the ‘props’ object has not changed. Notice how the entire Example() function runs till completion on every render.
const MyButton = React.memo(props=>{
console.log('firing from '+props.id);
return (<button onClick={props.eh}>{props.id}</button>);
});
function Example(){
const [a,setA] = React.useState(0);
const unmemoizedCallback = () => {};
const memoizedCallback = React.useCallback(()=>{},[]); // don’t forget []!
setTimeout(()=>{setA(a=>(a+1));},3000);
return (<React.Fragment>
<MyButton id="1" eh={unmemoizedCallback}/>
<MyButton id="2" eh={memoizedCallback}/>
<MyButton id="3" eh={()=>memoizedCallback}/>
</React.Fragment>);
}
ReactDOM.render(<Example/>,document.querySelector("div"));

Categories

Resources