React - re-rendering of the component via hook - reactjs

How can I make a Hook state change to re-render only the render of the Hook and not the component? I went around this by adding logic to the null returning middleware component, but I don't know if it's a good idea.
import { useState, useMemo, useEffect } from "react";
import "./styles.css";
function useTime() {
const [a, setA] = useState(0);
console.log("good-render", a);
useEffect(() => {
setInterval(() => {
setA(a + 1);
}, 3000);
}, []);
}
function Aaa() {
useTime();
return <></>;
}
export default function App() {
console.log("bad-render");
return (
<div className="App">
<Aaa />
</div>
);
}

Insofar as I'm aware, you can't stop a component rendering from a hook. Whenever the state in a component changes, the component gets set to re-render, which calls all hooks in it. In fact, conditionally calling hooks would break the rules of hooks.
Your best bet would be to encapsulate all the logic you need in refs and effects if you don't want the custom hook to cause a re-render. That being said, unless you have other issues with your code (i.e. a lot of really complicated and time consuming code), then a few extra re-renders isn't the worst thing in the world.
There's definitely a point to the idea that premature optimization is a problem.
That being said, different approaches for preventing the entire tree from re-rendering would be:
Encapsulate the logic in a separate component without children (as you show above)
Use React.memo on the components that make up the children so
React can know it should check for changes by reference on all the
props to determine whether to re-render.

Related

Dispatching actions inside inner functions of funtional component

I read that inner functions in statefull functional component should be defined using useCallback when we use setState or that function is passed as prop to child component. But what about dispatching actions? Do we need to use 'useCallback' there also?
import React from "react";
import { logout } from "../../../../actions/auth";
import { useDispatch } from "react-redux";
function Navbar (props) {
...
const dispatch = useDispatch();
const handleClick = () => {
dispatch(logout());
}
return (
<div>
<button onClick={handleClick}>Logout</button>
</div>
)
}
So first of all you need to know why you sometimes need useCallback around your functions and listeners.
The powerful useMemo and useCallback hooks create a value which is referentially stable (prev === current) between re-renders under the condition that the inputs (or “dependencies”) arrays doesn’t change its values (again, referentially). This allows children components you pass your values to to memoize themselves with React.memo and similar tools.
Why would you need to let them memoize? For performance reasons. And the first rule of performance optimization is you don’t optimize prematurely. So, do not blindly wrap everything in useCallback and useMemo: write your code and only once you reach some performance bottleneck investigate and eventually solve with those hooks.
To answer your question, do dispatch functions need to be wrapped? As I infer from your code we are talking about React Redux’ dispatch. The answer is no: React Redux dispatch, useReducer’s dispatch, useState’s updater function are all referentially stable.
dispatch does not effect to your inner functions. Your inner functions will be re-created when you create it without useCallback or with useCallback but dependencies changed
You can put dispatch as a dependency of useCallback and use it like normal function
const handleClick = useCallback(() => {
dispatch(logout());
}, [dispatch])

React useCallback with onClick not working. Rerenders child component

TimeChild re-renders in below image even after using useCallback
When Time sets state, then Time is going to rerender. Then, unless you do something to stop it, all of its children will rerender too. If you want to stop a child from rerendering, then the child component needs to use React.memo.
const TimeChild = React.memo(() => {
// ...
});
If you do this, then when TimeChild would render, it will first do an === comparison between each of its old props and each of its new props. If they are all the same, TimeChild will skip rendering.
The only role that useCallback plays in this is if TimeChild receives a function as a prop. If it does, then you need to make sure it receives the same function each time, or React.Memo will never be able to skip rendering because its props keep changing. But in your example there are no props at all being passed to TimeChild, so useCallback is not necessary.
You can use 'useCallback' in this way :
import React, { useCallback, useState } from "react";
const App = () => {
const [count, setCount] = useState(0);
const callBckValue = useCallback(() => {
setCount((count) => count + 1);
}, []);
return (
<div>
<h2>{count}</h2>
<button type="button" onClick={callBckValue}>
Click Me
</button>
</div>
);
};
export default App;
Firstly, you need to be aware, only in special situation, it makes sense to stop child component from re-rendering. If your case is not that special, that might not be a good idea.
Secondly, if you are sure you have to do it, use React.memo, the usage is pretty like componentShouldUpdate in class component

How to force an update for a functional component?

I'm learning redux and want to find out how useSelector updates a component, because the component doesn't have its own state.
I understand that useSelector() subscribes the component to the store, and when the store is updated, the component also updates.
Class components have this.forceUpdate(), but functional components don't have it.
How does one force-update a functional component?
You can simply do this
Add a dummy state that you can change to reliably initiate a re-render.
const [rerender, setRerender] = useState(false);
...
//And whenever you want to re-render, you can do this
setRerender(!rerender);
And this will re-render the component, since components always re-render on state change
The react-redux package relies on the rendering engine of react/react-dom to trigger the re-render of a given component that uses the useSelector hook.
If you take a look at the source of useSelector you can notice the use of useReducer:
const [, forceRender] = useReducer((s) => s + 1, 0)
As the name (forceRender) implies, redux uses this to trigger a re-render by react.
With v8 of react-redux the implementation of this mechanism changes but still relies on react-hooks for the re-render.
If you are curious how React handles re-renders, take a look at this excellent SO answer. It provides a great entry on the implementation details of how react-hooks are associated with the calling component.
I don't repeat Ryan here, but to sum it up:
The renderer keeps a reference to the component that is currently rendered. All hooks being executed during this render (no matter how deeply nested in custom-hooks they are) ultimately belong to this component.
So, the useReducer is associated with the component within which you called useSelector.
The dispatch function of useReducer triggers a re-render of this component (React either calls the render() method of a class-component or executes the function body of a functional component).
If you are curious how react-redux determines when it should force this re-render (by utilizing useReducer), take another look at the source code of useSelector.
Redux uses the subscriber-pattern to get notified of updates to the state. If the root-state of redux is updated the following things happen:
useSelector hooks in your application re-run their selector function
This re-selected state is compared to the previously selected state (by default via === comparison). The second argument to useSelector can be a comparison function to change this behavior
If the re-selected state differs from the previously selected state, a re-render is triggered via the useReducer hook.
The subscriber pattern is very react-like but potentially helps save many re-renders. Calling several useSelector hooks is cheap when compared with re-renders.
First of all, I want to mention that you don't need to do a force update when you use useSelector hook. Rerender will happen automatically whenever the selected state value will be updated.
But if you need to force update the functional component you can use this approach.
import React, { useState } from 'react';
//create your forceUpdate hook
function useForceUpdate(){
const [value, setValue] = useState(0); // integer state
return () => setValue(value => ++value); // update the state to force render
}
function MyComponent() {
// call your hook here
const forceUpdate = useForceUpdate();
return (
<div>
{/*Clicking on the button will force to re-render like force update does */}
<button onClick={forceUpdate}>
Click to re-render
</button>
</div>
);
}
I highly recommend avoiding the use of this hack, in 99% of issues you can resolve them without force update. But in any case, it's good to know that there is such a possibility in the functional component exists too.
Maybe something like this could help you:
In a Class Component you could pass a property like the one below...
<Element onSomethingHappen={
()=>{
if(shouldComponentUpdate())
this.forceUpdate();
}}/>
In the function component you can call the updater like this one:
function FunctionComponent(props){
//When you need it you can update like this one...
props.onSomethingHappen();
// Here you are ;) let me know if this helps you
}
Continuing on other answers, to keep your code clean you can create a dummy state and then set it in your own forceUpdate function:
const [helper, setHelper] = useState(false);
function forceUpdate(){
setHelper(!helper);
}
Now you can just call forceUpdate() in the rest of your code:
<div onClick={() => forceUpdate()} />

When sould I use useCallback in React?

It's really difficult for me to decide whether to use useCallback or not.
If there isn't an expensive function, should I just omit it?
(But I don't know whether a function is expensive or not...)
Or when a component is re-rendered frequently, I could wrap every function in it by useCallback?
Any idea?
You can consider following use case regarding using useCallback
React's useCallback Hook can be used to optimize the rendering behavior of your React function components.
Usually useCallback is very helpful during passing callback props to child components.
Let's say if a child component that accepts a callback relies on a referential equality check to prevent unnecessary re-renders when its props change, then it is important that any callback props do not change between renders.
To do this, the parent component can wrap the callback prop in useCallback and be guaranteed that it is passing the same function object down into the optimised child component.
Let's say you have a component that renders a big list of items.
import React from 'react';
import useSearch from './fetch-items';
function ListItem({ value, handleClick }) {
const items = useSearch(value);
const itemToElement = item => <div onClick={handleClick}>{item}</div>;
return <div>{items.map(itemToElement)}</div>;
}
export default React.memo(ListItem);
Here, ListItem renders a list of items. Let's imagine the list could be big, probably a few thousands of items. To preserve the list re-rendering, you wrap it into React.memo.
The parent component of ListItem needs provides a handler function when an item is clicked.
import React, { useCallback } from 'react';
export default function ParentComponent({ value }) {
const handleClick = useCallback(item => {
console.log('List item is clicked', item);
}, [value]);
return (
<ListItem
value={value}
handleClick={handleClick}
/>
);
}
handleClick callback is memoizied by useCallback(). As long as term variable stays the same, useCallback() returns the same function instance.
Even if for some reason ParentComponent component re-renders, handleClick stays the same and doesn’t break the memoization of ListItem.
Notes: Please don't mix React's useCallback Hook with React's memo API. useCallback is used to memoize functions whereas React memo is used to wrap React components to prevent re-renderings. They provide two different functionality.

React Hooks - where to put the logic that process passed props only once?

Sometimes a component needs to process passed props and save in its state. Since the processing can be heavy it's good to be done just once. Before hooks, it's typically done in constructor or componentDidMount.
Now coming into hooks, it can be achieved with useEffect, passing [] as the second parameter to run only once but I feel it's not the best place - what we're doing is processing props and save in state, which is not a Side Effect. From the docs: "Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects." Don't think pre-process belongs any of these.
So where's the best place to do it with hooks?
import React, {useMemo} from 'react';
const someExpensiveFunction = (a, b) => {
// expensive stuff happens here
}
const someFunctionalComponent = ({ prop1, prop2 }) => {
const someVariableDependentOnProps = useMemo(() => someExpensiveFunction(prop1, prop2), [prop1, prop2]);
return <div>{someVariableDependentOnProps}</div>
}
According to the docs:
useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
https://reactjs.org/docs/hooks-reference.html#usememo

Resources