when should I use useRef or useState in reactjs - reactjs

Now I'm on changing class based component into functional component, and there's instances which I should determine to use useRef or useState.
I already tried to get my answers using stackoverflow Q&As, it keeps me wondering.
What I understood is useRef and useState keeps value during whole apps, and useState cause rerendering and useRef is not.
we needs rerendering process if there's some changes in view, If so, Should we create whole variables with useRef which is not in return( <> ...</>)> ?

Generally Ref is used when you dont want to component re-render again but you want value in some form so that you can use later. In layman term if you just want to play with dom related stuff like updating width, height etc.
Common example for using ref:
When you want to focus in and out your input without using state(as you dont want to render the component again)
Updating dynamically style(Fox ex: You create your accordian then you want to update heigh and show transition when according open and close(if you dont want to show you can avoid this example))
If you want to create this type of utilities then you dont need to play with states you can do only with ref

for most use cases you want to handle your react component's behavior through state provided from useState (or props where you get as parameter). that's the React way to go.
eventually there will be cases to useRef, where you may need for example to access a DOM element directly for some special reason. but they are exception cases. if you you see yourself calling useRef throughout your application you are most likely doing something wrong.

Related

How to limit scope of re-rendering to single component in React?

My components are structured looks like this. Each component is it's own functional component in its own file - this is just a schematic of how they interract.
<FirstLevelComponent>
// propValue is declared on this level with useState hook.
<SecondLevelComponent someProp={propValue}>
<ChildComponent1></ChildComponent1>
<ChildComponent2 someProp={propValue}></ChildComponent2>
<ChildComponent3></ChildComponent3>
</SecondLevelComponent>
</FirstLevelComponent>
someProp is declared in state in the FirstLevelComponent and is passed along to the SecondLevelComponent and when it changes, it triggers the re-render of the entire SecondLevelComponent. But the only dependency on that prop is in one of the children. The rest of the children are unaffected.
What would be the best way to isolate that behavior and only limit scope of re-rendering to the single child that depends on that prop?
Some constraints:
This is a huge production application so something like Just add redux would not be an easy solution.
Refactoring SecondLevelComponent will be a challenge (1500 lines of code), while I am open to such opportunity`, I am looking for the way to achieve this assuming it's not a hello world project. Solutions that are easy and ideal for application in its early stages are quite a rework when we are dealing with legacy code.
and only limit scope of re-rendering to the single child that depends on that prop?
What you're looking to accomplish goes against the nature of React. If a parent component has state, and that state changes, React will react and rerender the component.
Stopping the parent component from rerendering when its state changes is an anti pattern (as opposed to simply stopping its children from rerendering with useMemo).
The way I see it, you have the following options:
Add state to the child component instead. If it's the only component reliant on this state, it should be a simple refactor.
Replace useState with useRef in the parent component. Pass this to the child for creating initial state.
Option two could lead to other implications if anything else in the app is dependant on this piece of state.
Now, if you just want to keep the extra children from rerendering, which isn't exactly what I quoted above from the question, you could just simply use useMemo (and useCallback if passing a function as a prop)..
I saw the same problem, also big application
Option 1
You can take advantage of render from React composition if you can move propValue state and ChildComponent2 component into SecondLevelComponent
Explanation: the states in SecondLevelComponent wont rerender his childs like ChildComponent1 and ChildComponent3
Option 2
You can memoize you components using React.useMemo not React.memo because you are not passing propValue in ChildComponent1 and ChildComponent3 like this:
// How to memoize
const ChildComponent1Memoized = React.useMemo(()=><ChildComponent1/>,[propValue])
// In this case ChildComponent1Memoized will not rerender again
<SecondLevelComponent someProp={propValue}>
{ChildComponent1Memoized} // <---
<ChildComponent2 someProp={propValue}></ChildComponent2>
<ChildComponent3></ChildComponent3>
</SecondLevelComponent>
Extra
if you are passing functions as props be sure you are using useCallbak in every function, because they are rebuilding in every render, that cause Memo dosent work

React Routing causes inputs to lose focus

I have an app with a component that has inputs in it. When the user changes the inputs, I want to update the query string in the URL to reflect the new state. I achieve this by detecting a difference between the current location.search values and the query string that my state would generate. If there's a difference, I return a <Redirect> component with the query args reflecting the new state.
The problem is that after every such redirect, the component re-renders and my inputs are replaced by new inputs, meaning that the input I just changed no longer exists, so it doesn't have focus. If it's a text input, then it loses focus after every keystroke.
How I dealt with it was to remember the ID of the element that had focus when the state changed and then set focus to the new element based on document.getElementById once the component is done re-rendering.
I've reduced the issue down to a minimal app that is at https://codesandbox.io/s/github/TrevorMills/react-routing-conundrum. The problem manifests on both functional components and React.Component classes.
My mechanism seems hacky to me. My question to the React experts out there is, have I missed something? Is there a better way to get the state into the location query parms than <Redirect>? I tried adding key to my inputs but it didn't make a difference. If my mechanism seems legit, is there a way to apply it to the app on whole in a higher order component?
Thanks in advance.
Since you were using redirect, on every change of input value, the component rendering toggles between the returned value and the redirect causing all components to remount instead of re-render. This causes the input field to lose focus.
Avoid using <Redirect /> and use history.replace after updating the state.
Instead of doing history.replace in onChangeText function, you can write a useEffect, like this.
useEffect(() => {
props.history.replace(props.location.pathname + "?" + qs.stringify(state));
}, [state]);
Updated working example is here:
https://codesandbox.io/s/cocky-swirles-kcqwo?file=/src/App.js
I've edited your functional component version of the code, however the changes I made should be applicable to both.
The main thing I did is I removed the state duplication. Instead of duplicating the state from the route to the component, I just made the component use the query params directly as state. And then I made the updateState function update the query params using props.history.replace which is how React Router allows imperative navigation.
Hope this helps!
https://codesandbox.io/s/great-williamson-0pxyb

What is the use case of useMemo in React?

I need help to understand when to use useMemo hook in React vs useState + useEffect. From what I've understood after reading other questions on S.O. and websites :
With useState you can create a value (changeable through the setter) and you can use useEffect to do whatever you need if some of the dependencies listed in the dependency array changes. On first render for example your component will be rendered with the initial value passed to useState, then the effect will run and your component will re-render with the new value you set with the setter in your effect.
With useMemo you can create a value that will be computed from a function you give to the hook and it will change if some of the dependencies in the dependency array changes. So far, to me, it looks like useState + useEffect except you don't have the setter to change the value. On render your useMemo will run if one of the dependencies has changed. Difference here : your component will wait for the value to be computed before rendering (not sure about this part).
From what I've read, useMemo should be used for heavy operations. But if it's blocks the rendering of the component what's the point ?
Some says that with the dependency array you can avoid unnecessary updates but I can also do it with useEffect without blocking the render ? If I don't have a heavy component to render, isn't it faster to use useEffect ?
Thank you :)
Edit :
I accepted Philip Feldmann's answer as it fits my needs. As suggested in the comments you can take a look at this question for a global comparison :)
You are correct, useEffect runs after rendering as a side effect. That means that when you don't want to block the render loop, you can use your version.
That version will however trigger another rerender through calling the setter.
The initial time to paint might be faster, but you'll instantly block the render loop again by setting the new value, which in most cases should be slower than just using useMemo directly.

Editing a form is not working on react redux

I am new on react. I am working on react application with redux. I have a form (I am using redux-form) by which I can save data or edit data.
my problem is , In edit mode I populate data using componentWillReceiveProps. and populated perfectly, now when I try to clear any field on form its again fill.
componentWillReceiveProps(nextProps){
this.props.dispatch(initialize('NewProject', nextProps.project.project[0]));
}
I would be grateful for any help.
Is there a reason you're not dispatching this action somewhere else, like in componentDidMount? I can't say without seeing more code, but it's possible that whenever you edit your form, React again calls componentWillReceiveProps and overwrites whatever you did with the behavior you've given your component.
Per the React documentation:
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
It may be a good idea for you to move your dispatch to a more predictable event if possible, like componentDidMount. Typically, your render method should be capable of handling different cases if a component has multiple possible states. You could also create an edit and save version of your component, and render one or the other based on the props you receive. The best way to "populate" data, as you put it, is to define propTypes for your component, and then use your props to insert that data into elements in the render method.

React: Parent component re-renders all children, even those that haven't changed on state change

I haven't been able to find a clear answer to this, hope this isn't repetitive.
I am using React + Redux for a simple chat app. The app is comprised of an InputBar, MessageList, and Container component. The Container (as you might imagine) wraps the other two components and is connected to the store. The state of my messages, as well as current message (the message the user is currently typing) is held in the Redux store. Simplified structure:
class ContainerComponent extends Component {
...
render() {
return (
<div id="message-container">
<MessageList
messages={this.props.messages}
/>
<InputBar
currentMessage={this.props.currentMessage}
updateMessage={this.props.updateMessage}
onSubmit={this.props.addMessage}
/>
</div>
);
}
}
The issue I'm having occurs when updating the current message. Updating the current message triggers an action that updates the store, which updates the props passing through container and back to the InputBar component.
This works, however a side effect is that my MessageList component is getting re-rendered every time this happens. MessageList does not receive the current message and doesn't have any reason to update. This is a big issue because once the MessageList becomes big, the app becomes noticeably slower every time current message updates.
I've tried setting and updating the current message state directly within the InputBar component (so completely ignoring the Redux architecture) and that "fixes" the problem, however I would like to stick with Redux design pattern if possible.
My questions are:
If a parent component is updated, does React always update all the direct children within that component?
What is the right approach here?
If a parent component is updated, does React always update all the direct children within that component?
No. React will only re-render a component if shouldComponentUpdate() returns true. By default, that method always returns true to avoid any subtle bugs for newcomers (and as William B pointed out, the DOM won't actually update unless something changed, lowering the impact).
To prevent your sub-component from re-rendering unnecessarily, you need to implement the shouldComponentUpdate method in such a way that it only returns true when the data has actually changed. If this.props.messages is always the same array, it could be as simple as this:
shouldComponentUpdate(nextProps) {
return (this.props.messages !== nextProps.messages);
}
You may also want to do some sort of deep comparison or comparison of the message IDs or something, it depends on your requirements.
EDIT: After a few years many people are using functional components. If that's the case for you then you'll want to check out React.memo. By default functional components will re-render every time just like the default behavior of class components. To modify that behavior you can use React.memo() and optionally provide an areEqual() function.
If a parent component is updated, does React always update all the direct children within that component?
-> Yes , by default if parent changes all its direct children are re-rendered but that re-render doesn't necessarily changes the actual DOM , thats how React works , only visible changes are updated to real DOM.
What is the right approach here?
-> To prevent even re-rendering of virtual DOM so to boost your performance further you can follow any of the following techniques:
Apply ShouldComponentUpdate Lifecycle method - This is applied only if your child component is class based , you need to check the current props value with the prev props value ,and if they are true simply return false.
Use Pure Component -> This is just a shorter version to above method , again works with class based components
Use React memo -> this is the best way to prevent Rerendering even if you have functional components ,you simply need to wrap your components export with React.memo like : export default React.memo(MessageList)
Hope that helps!
If parent component props have changed it will re-render all of its children which are made using React.Component statement.
Try making your <MessageList> component a React.PureComponent to evade this.
According to React docs: In the future React may treat shouldComponentUpdate() as a hint rather than a strict directive, and returning false may still result in a re-rendering of the component. check this link for more info
Hope this helps anyone who is looking for the right way to fix this.
If you're using map to render child components and using a unique key on them (something like uuid()), maybe switch back to using the i from the map as key. It might solve the re-rendering issue.
Not sure about this approach, but sometimes it fixes the issue

Resources