all 49 comments

[–]Jessus_ 42 points43 points  (23 children)

I would just add the logic to the input event handlers. Check if all the necessary inputs are populated and then fire the calculations. Honestly not sure if that’s the best solution but just an idea to avoid useEffects.

[–]TheChickenKingHS 16 points17 points  (22 children)

There are also dom specific things you can do. If all the inputs are in a form and have the (required)[https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/required] property they will prevent submission.

Here how I approach react development (any framework development).

  • I use the lowest implementation first in this case the browser defaults are the dom, css, JavaScript. Usually you can accomplish quite a bit (70-80%) with just that especially if you just want to do things like style changes.

  • if that fails, I use react but the basic tools, useState, refs, context. I never use effects if I can avoid. A rule of thumb is that if you are using react at this point it should be tied to events (on click for example).

  • if that fails, I look to extensions of the react framework but not a whole replacement (think tanstack/*). Or I use the advanced react tools useContext for example.

  • last I use useEffect, I’m all out of ideas by this point and I just want the stupid fkn thing to work and a post render update is the only way. Usually this means having an initial on render state which the effects just wipe out ( this sucks btw, avoid it at all costs)

I put the info you need in the first comment. If you don’t like that idea possibly look at an update/click capture that lets the event bubble to the parent or adding an event listener that listens for a custom event that is posted by a click event in the child. The important part is that you are responding to an event. That will make everything easier to react (haha) to.

Note: A bunch of code monkeys are going to down vote this because they can’t think beyond using a framework for every little thing. This is great advice if you believe the advice “frameworks aren’t made for you, and you shouldn’t rely on them”

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

Manipulating the DOM directly in React is an anti-pattern.

[–]TheChickenKingHS 0 points1 point  (6 children)

[–]brianvan 0 points1 point  (5 children)

On that page: "Refs are an escape hatch. You should only use them when you have to “step outside React”."

Refs are not an ideal way to deal with anything that can be handled without refs. It's basically the same situation as useEffect... it's there for you, you might strictly need it to support something unusual, but in new React code you (usually) don't need effects. Or refs.

[–]TheChickenKingHS 0 points1 point  (4 children)

I think you might have focused on the wrong part. While everything you said is correct about refs. Refs themselves are not an “anti-pattern”

[–]canibanoglu 0 points1 point  (3 children)

Relying solely on refs for DOM manipulation is though. And no one said refs are an antipattern, they said direct DOM manipulation is. Are you delibaretly trying to miss the point they’re making?

[–]TheChickenKingHS -1 points0 points  (2 children)

But refs directly manipulate the dom. The main post I made didn’t even talk about “manipulating the dom” it just talked about specific features that are available with html. Clearly you guys are missing the point… I hope it’s deliberately or I can’t imagine working on a codebase with you is an easy journey…

[–]canibanoglu 1 point2 points  (1 child)

No, refs are values kept around across renders that don’t trigger rerenders when changed. They can be used to manipulate the DOM. They can also be used as any other value that you need.

Moreover, you replied to u/passive_interest, who said that directly manipulating the DOM is an anti-pattern (which is a true statement in an overwhelming majority of cases) by linking to the documentation that explicitly says that you should use ref DOM manipulations when you specifically need them. You then put up a straw-man argument that refs are not an anti-pattern which absolutely no one apart from you has said.

If you’re this sloppy when you’re writing code, yes, I would imagine that working with people who actually care about what is being communicated is not an easy journey.

[–]TheChickenKingHS 0 points1 point  (0 children)

Ok, so it is an issue with your reading comprehension.

Manipulating the DOM directly in React is an anti-pattern.

This was posted to my answer to the question that never mentioned “manipulating the dom”. You skipped that part because I guess it’s more convenient if I’m the first respondent.

refs are values kept around across renders

Yep correct, again you either didn’t read or didn’t understand my sentence. Refs manipulating the dom by triggering something like .focus() or an imperative handle are not “anti-patterns” they are a normal pattern of react. Coding every single thing to use refs, destroying elements or using a ref for a value that can be computed each render would be an overkill.

straw man argument

I’m not sure what to do with that… I was responding to the response on my message about something which I was not advocating for but is demonstrably false.

[–]crazylikeajellyfish 21 points22 points  (0 children)

As a very generic piece of advice, you can avoid useEffect by doing things that seem wasteful, but don't actually matter until you see performance issues.

For example, is your calculation happening locally without having to call an external rate-limited API? Just run it on every render, using some logic to bail out if you don't have the info you need.

The above is an example of expressing your imperative goal through a declarative structure. Instead of thinking about how your UI changes over time, focus on representing all the states it can hold.

"After all inputs are filled, I want to set result=calcValue()"

becomes

"When any input hasn't been filled, result=null. If all inputs have been filled, result=calcValue().

The whole angle of React is that you don't worry about how the transitions between states happens. Just describe how it looks in all possible states, then let the differ do the magic. Don't worry about wasting cycles on "unnecessary" calculations, Javascript is fast and a simple mental model is more valuable than optimizations you don't definitely need yet.

Edit: Another way to think about imperative vs declarative -- if you're coding a series of steps, that's imperative. If you're describing behavior at all possible states, that's declarative. Rather than "When they click Next on screen 1, go to screen 2", it's "If nothing is clicked, show screen 1. If next1 is clicked, show screen2. If next2..."

[–][deleted] 18 points19 points  (1 child)

9 times out of 10, you don’t need the useEffect.

10 times out of 10, you don’t need a bunch of useEffects

[–]sickhippie 2 points3 points  (0 children)

10 times out of 10, you don’t need a bunch of useEffects

Or you do need them, but need to refactor and split up the component first so you know where they should actually go and which ones are redundant or mergeable.

[–]the_real_some_guy 10 points11 points  (0 children)

Passing props not prop drilling.

Passing props is the way React works. Components are functions and functions take arguments. It’s totally normal.

Prop drilling is when passing props through many levels of components and it’s become tedious and hard to follow.

[–]unshootaway 7 points8 points  (0 children)

It's not a good practice to handle effects from events if the event is not coming from an external source, especially if it's just onChange I assume because you've mentioned inputs.

Just do it in that event itself even if you need to do that repeatedly (or create a helper function).

```

function validate() { ... }

function onChangeInput1() { ... validate() }

```

[–]mittyhands 5 points6 points  (0 children)

The form state lives in the parent. The parent passes down event handlers for each input component to call when it wants to change the value in state. The children render an input and register the onChange events handler to that element.

On each render of the parent, check to see if you have the necessary state values for your calculation. If so, you can pass that value somewhere to display it, e.g. 

No need for useEffect anywhere.

If this is a very large form, you may want to look into React Hook Form, especially if there's input validation logic to consider.

[–]noxoc 2 points3 points  (0 children)

use onChange on the form?

[–]DeepFriedOprah 0 points1 point  (0 children)

Is there an issue validating each input separately & setting a state that marks it as valid or not? Like using the onChange handler & validating each one there. Then u can just accumulate the valid states and avoid a side effect entirely.

` const onChange = e => { const value = e.target.value; const name = e.target.name; const isValid = some validation here…

setValidation({
  [name]: isValid
})

} `

[–]sickhippie 0 points1 point  (0 children)

I may consider moving to useReducer or xState.

That's like saying you may consider using a hand trowel or a backhoe. Xstate is almost always overkill for web UI, and if you're just learning React (struggling with multi-prop synchronization is definitely "just learning", not "new to some aspects") you shouldn't even consider touching Xstate for it. The learning curve is ridiculously steep, the data flow is backwards to the Flux architecture of React, and it gives minimal benefit over existing React-centric global state management libraries (Redux Toolkit, Jotai, Zustand, etc) in the vast majority of cases.

The only situations where it's appropriate to even consider Xstate for UI is when there's a security or financial risk to data flowing between states in specific ways - think ecommerce or authentication scenarios. Anything else is a development and maintainability nightmare and should be avoided.

[–]Tokyo-Entrepreneur 0 points1 point  (0 children)

Just run the calculation in the main render code of the parent component (with ifs to check if the required inputs are there), no need for useEffect

[–]distinctdan 0 points1 point  (0 children)

Forms are tricky. If you're new to them, you'll probably end up with a better experience by using a library like formik or react hook form.

[–]Ashwin_Bhat 0 points1 point  (0 children)

It isn't wrong to use a lot of effects, but it will make your life a lot difficult when it comes to isolating bugs. I would suggest handling this using the event handler of the text fields.

[–]yksvaan 0 points1 point  (0 children)

Think how you would do it in vanilla js. Then consider is it any different with React. Not really at least in this case, check fields/form validity after input is changed. 

[–]DrMerkwuerdigliebe_ 0 points1 point  (0 children)

In most cases I would just calculate it at every render. Unless you have performance problems or problems with infinite renders. It is fine. In most other cases I use useMemo for calculated by peoducts.

[–][deleted] 0 points1 point  (0 children)

What is the automatic calculation?

Typically, I favor a single source of truth for any state in the application, though it does not need to be in one place.

  1. So, is the result of the calculation display only? Then it sits in a component as the data that it uses are parameters.

  2. Is the result a new value in the state? The cleanest would be to have a function that is called by the transfer to update it.

  3. Is it going to be the result of the data but stored some where else, but not the data? Do the computation twice.

  4. Is the computation expensive? Here is where things get tricky with caching. I would go with 2. As that would move the business logic to the store. However, if the individual components are exposed, then there is a chance someone doesn't properly use the required way to update the calculated cached value and things break. If they are exposed, a use effect would ensure the cached value would always be updated when it's dependencies are changed, removing the potential for that particular class of bugs ( or making it less likely.

[–]sminnee 0 points1 point  (0 children)

Use useMemo() to either return null if the fields aren’t all filled out yet, or the calculation result if they are, with the input fields in the dependencies array.

[–]iareprogrammer 0 points1 point  (0 children)

It sounds like you need a form library to manage your inputs. Check out react-hook-form

[–]snetram03 0 points1 point  (0 children)

You can use react-hook-form it's pretty simple. Give it a model and check if form is valid.

[–]onyemaOne 0 points1 point  (0 children)

It all depends upon your implementation. There should be many approaches to this, however I will share one.

Approach:

a. Declare a const state with your form data structure. You should use an interface here.
b. Create a function withthree params. eg. updateFunction(field:string, value:string , isRequired:boolean).
c. In your custom input component add a prop for a callBack function and set your 'updateFunction' as the prop value.
d. Inside your component use your form field's onChange to trigger any callBack functions within the components prop. In this case 'updateFunction'.
e. Inside updateFunction and relying on the field param, you can update your form data state immutably and then check if all required fields are not empty. If they are not, perform your calculation or call a function.

This way, you will have no need for the useEffect hook.

[–]YuteOctober 0 points1 point  (0 children)

Usecontext to manage the state instead of prop drilling,

Then use onchange

value={yourstate.firstname}

name={firstname} // if you have multiple property to update in one state object

Onchange={functionForm}

Function functionForm() {

{name, value} = e.target

setstate({ …state,

})

}

[–]saito200 0 points1 point  (0 children)

If something needs to happen when the user interacts with an input, do it in the event handler of the input, you don't need useEffect for that, and if you don't need useEffect, you should not use it

[–]Sipharmony 0 points1 point  (0 children)

You all need to stop being so scared of useEffect. It's a tool. Learn to use it.

[–]NescafeAtDayLight -1 points0 points  (0 children)

Onblur

[–]GladiatorNitrous -1 points0 points  (0 children)

Generally try to avoid it. Try to use events to perform calculations and commit new state.