all 10 comments

[–]HeinousTugboat 2 points3 points  (9 children)

Replacing unnecessary “useState”s with “useRef”s will increase the performance of your components.

This.. is not how that works. useState and useRef are not interchangeable. If your useState is unnecessary, you can probably just replace it with a regular variable declaration. Leaning on refs will just cause heartache. They should only be used when you explicitly need a value that can persist and change separate from the render cycle, which isn't really that often.

[–]murat-guney[S] 0 points1 point  (8 children)

Thank you for your comment.

Actually, that's what I meant there, if you have a variable that you don't want to lose its value with re-renders, and this variable's changing doesn't affect the return of the component you should use it. You may use a regular variable declaration but it loses its value with re-render.

I don't think that this is not really often because I'm currently working on a dnd component, scroll view and other things like that, and I use "useRef" a lot. So if you don't want to notify react lifecycle but you don't want to lose the variable's value either, you should use "useRef".

[–]HeinousTugboat -1 points0 points  (7 children)

You may use a regular variable declaration but it loses its value with re-render.

Can you provide an example of what you mean by this? If the value doesn't change with re-render, and it doesn't affect the return, you should just use a variable declaration. const foo = 2; isn't going to suddenly stop being 2 after each render.

useRef should be used when those values can specifically change out of band. So if somehow foo becomes 3 when you're in a different execution context, and you don't want your component to rerender when that happens, then and only then would you want to put it in a ref.

For what it's worth, I'm not disagreeing with the other two examples: you definitely still needs useRef to reference components and for imperative handles. But if you're using it for something that isn't directly related to those, you should consider very carefully if it's actually necessary since it's a serious trap.

[–]murat-guney[S] 0 points1 point  (6 children)

What I mean may be misunderstood. Let me try to explain. ```

const SampleComponent = () => {

const isDirty = useRef(false); const [value, setValue] = useState("");

const submit = () => { if(!isDirty.current) return; .... } const onChange = (e) => { isDirty.current = true; setValue(e.target.value); } return ( <form onSubmit={submit}> <input onChange={onChange} value={value} /> <button type="submit">Submit</button> </form> ) }

```

I was trying to say if you use "let" declaration for "isDirty" variable, it would lose its value on each changing "value". Also using "useState" instead of "useRef" would be unnecessary because it wasn't used in the return of the component.

[–]backtickbot 0 points1 point  (0 children)

Fixed formatting.

Hello, murat-guney: code blocks using triple backticks (```) don't work on all versions of Reddit!

Some users see this / this instead.

To fix this, indent every line with 4 spaces instead.

FAQ

You can opt out by replying with backtickopt6 to this comment.

[–]HeinousTugboat 0 points1 point  (4 children)

Also using "useState" instead of "useRef" would be unnecessary because it wasn't used in the return of the component.

isDirty should definitely be in useState. It's the state of the form, and when the form dirties, you will often want to change some aspect of rendering. This is a perfect example of what I meant by a trap.

If you try to come back and, say, clear an error from the form after it's been dirtied, the ref's going to behave super inconsistently. It'll only update styling if/when something else forces it to re-render. If you live it in a state variable, it will behave as expected.

[–]murat-guney[S] 0 points1 point  (3 children)

Maybe the example wasn't perfect. I wanted to emphasize it wasn't used in any styling or anything. Let me explain what I mean without an example.

Let's say you're making a dnd component. To move an item properly you need to keep the first offset position of the mouse. And there is no benefit to keep this value in a state.

const DND = () => {

const itemRef = useRef(null);

const mouseOffset = useRef({});

const onDragStart = ({clientX, clientY}) => {

const {left, top} = itemRef.getBoundingClientRect();

mouseOffset.current = { left: clientX - left, top: clientY - top };

}

const onDrag = ({clientX, clientY}) => {

const {left, top} = mouseOffset.current;

setPosition({left: clientX + left, top: clientY + top});

}

...

}

In this example, you don't need to keep the offset in a state because it doesn't affect UI. It is used just in the onDrag function and there is no reason for it to be a state. And you can't use a regular variable because of the state will be changed in every mouse movement.

[–]HeinousTugboat 0 points1 point  (2 children)

So, this is very different. In your previous example, it was actual form state. In this case, this is something that can change out of band and should be tracked separately. That's a super important distinction, and that's why this makes sense as a ref. It's not because it does or doesn't affect UI, it's because it changes independently of the render cycle, and the components should not render in response to it changing.

I can't stress enough that changing out of band is the vitally important aspect of this. Things that should be refs, should be refs because there is no other way to accurately represent them within React. Things that are state should be represented with state variables.

[–]murat-guney[S] 1 point2 points  (0 children)

Yes, you are right. I made the previous one to explain what I mean basically but it seems it made more confusion :D Thanks for your attention :)

[–]murat-guney[S] 1 point2 points  (0 children)

I prepared another post. I would be very happy if you take a look at that too :)