What's the best way to avoid react-hooks/exhaustive-deps? - javascript

I currently use the useEffect hook to run some function or update a variable as an effect of another value/variable being changed
example:
useEffect(() => {
setValues(types[formStep]]);
}, [formStep]);
I am using the above code to update a variable after another variable has changed, however i keep getting the warning that
React Hook useEffect has a missing dependency 'types
and obviosuly when i include 'types' as a dependency, the application falls into a loop and i get the following error
Maximum update depth exceeded.
What is the best way to achieve the variable update but avoid the error? not just for this example for for all cases where you need to update a variable depending on another variable changing before it.

Some solutions found here: https://www.benmvp.com/blog/object-array-dependencies-react-useEffect-hook/.
Another option could be to stringify the dependency so that it is not treated as a different object everytime.
useEffect(() => {}, [JSON.stringify(data)])

What is the best way to achieve the variable update but avoid the error?
It depends on a lot of factors: sometimes using a ref (if you don't want to trigger an update), sometimes you are just deriving state and there is not need for use-effect (I think that this could be your case), other times you need to trigger an update only when certain value(s) has changed... Sometimes the next value depends on the previous one so you should use the function overload of setState and avoid passing the previous value as a dependency... There is not a generic answer to this problem.
It's difficult to know what's happening in your case without more context. However, I think that I have an idea of what could be happening... This is just a shot in the dark.
Is types an Object that you are defining inside the function of the component? Can you move that definition outside of the function of the component? Because if you can do that, then there is no need to pass it as a dependency.
Also, do you really need to use useState and useEffect for setting values? Do you really need to trigger another update? can't you just do this?
const values = types[formStep];

Related

Should we use class variable in react native?

I tend to avoid using class variable as much as possible, likely 99% of my codes are done with state. Always have an impression that, we should avoid using this because React re-render UI when there are change to state or props only.
Recently my colleague started using class variable, for storing some hardcoded value like following. I don't see why we can't store this hardcoded value within state, although the value actually never gets updated in app. And this type of value assigning still works.
this.Arr = [
{ option: 'Product'},
{ option: 'Technical'},
{ option: 'Enquiry'},
{ option: 'Report'},
];
So, should we use class variable in react native? What's your supporting argument here?
Update
After some further readings from the following questions, I came into a conclusion of..
Readings:
ReactJS - What is difference between component state and class variable?
Should a React component use (fixed) values not on state or props?
Conclusion:
As always, keep variable that possibly mutate inside State Variable.
It's fine to store hardcoded value with Class Variable. However, it might make sense to move them to a constant file where you read all the fix value from there.
Using Class Variable is not an anti-pattern of ReactJS/ReactNative. Since the value does not require UI to re-render, hence storing in Class Variable is not an issue.
Appreciate anyone to comment further. Thanks.
If this variable is something that will never change and doesn't depend on anything that exists in the scope of your component, I'd suggest storing it as a constant outside of your component instead.
If you do need to generate it based on things that are in the scope of your component, you need to store it either as a state or as a class variable.
I would recommend that you store it as a state only if you need to update your UI when that variable changes (with setState). If you don't need that, it's better to store as a class variable. And the reason is that if you store it on a state, without actually requiring a UI update when that variable changes, you will end up causing unnecessary re-renders, that may be expensive.
Also, storing something that is by definition not a state, inside a state, is more confusing in my opinion than storing it separately as a class variable.
Btw, If you're working in a project that allows you to move from class components to hooks, I'd strongly suggest so.

React Storing data for complicated processing

I am somewhat new to ReactJS.
I want to store data for complicated computations in ReactJS. Since the setState is asynchronous, I cannot be bothered to store all the required data inside state as it would be next to impossible to do what I want.
I was thinking of doing the following: store the needed variables inside an object inside the state itself. E.g the state defined in the constructor is the following object
constructor(props){
this.state = { complicated_storage: { x : 123, y : 324 } }
}
This way I can manipulate this.state.compicated_storage.x or this.state.compicated_storage.y, and do stuff like this.state.compicated_storage.x = 4 without worrying about asynchronous behavior.
Is this a bad way of doing stuff in ReactJS. I noticed that with this you can bypass the entire setState mechanism, and just store all my variables inside the object within the state.
Thank You
Is this a bad way of doing stuff in ReactJS. I noticed that with this you can bypass the entire setState mechanism, and just store all my variables inside the object within the state.
The entire purpose of state is re-rendering components whenever it changes. If you modify it directly, such as state.someVariable = 2, React will not know about your change, and it will not rerender your components after the change (however, the change will be reflected if something else later causes a rerender).
If you need a change to some data trigger a rerender and update the view, use state together with setState.
If not, you can simply use a class field, rather than store it inside state.
Besides, I'm not sure in what way asynchrony you think will impede your goals, so perhaps you should be clearer there. State being asynchronous just means that changes to state are made "a little later" after the function is called (it's a whole other topic), so React can batch updates for performance reasons.
Last but not least, you should look into hooks (useState), instead of using class components.

Should i pass functions to my component manually or using ref attribute?

I have built my own FormBuilder component (which is almost 5k line now), so it can cover all my needs, one issue i'm encountering is loading a new set of FormData any time the user needs, since my FormBuilder can accept a set of nested components, its kinda hard comparing the changes, and handling everything in component did mount, one easy way is building a loadFormData method inside the FormBuilder and calling this method from outside the component
I know this can easily be accomplished using ref attribute, but react highly suggest avoiding this and i got discouraged! and i was not sure if i should do this or not!
So i came up with a new alternative which kinda does the same thing:
class A {
onGetFormBuilderInternalFunction = (functions) => {
this.formBuilderFunctions = functions
}
onLoadButtonClick = () => {
this.formBuilderFunctions.loadFormData(someNewData)
}
render () {
<FormBuilder onGetInternalFunction={this.onGetFormBuilderInternalFunction}
}
}
class FormBuilder {
componentDidMount() {
if (this.props.onGetInternalFunction) {
this.props.onGetInternalFunction({
loadFormData: this.loadFormData,
})
}
}
}
So what do you think ?, is this a better approach ? or is this still a bad one ?, i was thinking using this method will at least only gives access to functions i need and not everything else.
I should also mention, loadFormData is simply one example, and there are at least a couple of these special function which i really really think is best to simply call from outside, so even if i do somehow pass the new data from props and handle it in componentDidUpdate, for the rest of these functions, i still need to access the FormBuilder
Yes that is the correct way to do it. However, if you have a 5,000 line component I would strongly suggest breaking it up into smaller sub components.
Keep state and handler functions in Parent and pass it down to FormBuilder as props.
I'm not sure how your component is set up so these are just thoughts based on a vague understanding.
Generally, you wouldn't want to do either of those approaches. Instead, the recommended approach is to pass someNewData to FormBuilder as one or more props (I'm guessing one prop, in your case).
Avoid using refs for anything that can be done declaratively.
– React Docs: When to Use Refs
This sort of scenario is discussed in Lifting State Up:
Instead of trying to sync the state between different components, you should rely on the top-down data flow.
– React Docs: Lifting State Up # Lessons Learned
If you're keeping track of changed state separate from your initial data, then you just need to reset that state when the form data (someNewData) changes. However, that's not the recommended approach.
Instead, uncontrolled components might be a great fit. By setting defaultValue to the initial data (i.e. someNewData.someField) and then letting the component deal with the value until you need it (e.g. when submit is clicked read the value from the component), you might be able to simplify your FormBuilder. Note: if you want to reset the uncontrolled component when your initial data (someNewData.someField) changes, you will need to use a key prop on the uncontrolled component with the value based someNewData.someField.
If you want to “reset” some state when a prop changes, consider either making a component fully controlled or fully uncontrolled with a key instead.
– React Docs: React Component # static getDerivedStateFromProps()
In response to your main question:
Better as you are doing right now than using ref.
I base this answer on what the react documentation says in the last paragraph of the "ref" glossary description and that the current approach can also be supported in case FormBuilder is refactored to be a functional component in the future (with Hooks and so), while using "ref" wouldn't.

Lots of logic in getDerivedStateFromProps, skinny vs fat gDSFP

I'm wondering if it is bad practice to have 'fat' gDSFP functions. Currently, I have a component that takes in some data and does a bunch of data manipulation
function getDrivedStateFromProps(nextProps, prevState) {
// start doing data manipulation
and along the way if it encounters an error condition it returns a new error slice of state
const someValue = nextProps.something * myFunc()
if (someValue === badThing()) {
return {error: true};
}
// continue doing data manipulation
This repeats several times before it finishes all data manipulation and returns the derivedState that my component needs. I'm curious on what the communities views are on a 'fat' gDSFP functions. My component only runs gDSFPs when the external data source has changed and it needs to derive new state so I don't see where else this logic could live.
I think you may actually be in a situation where using getDerivedStateFromProps might not be the best approach at all. In your question you state...
My component only runs gDSFPs when the external data source has changed and it needs to derive new state so I don't see where else this logic could live.
Given this statement, it sounds like you can use memoization. Here is this concept discussed on the react docs.
The basic idea is that managing getDerivedStateFromProps can get hairy especially when you have lots of logic going on there. If the reason why you want to capture your props in state is only to get a performance boost, then memoization might be your friend.
The idea here is that you do not want some logic to run every time your props change, so what this will buy you is that if you have a function whose arguments are the same as they were before, as an example state has not changed, your function will return the last computed result which it had stored in the cache. This library handles this beautifully and is pretty easy to use.
Another concern people often have which may prompt them to reach for getDerivedStateFromProps is to ensure that their component does not render unless if the props have in fact changed, so by having the component render based off of computed state this can be achieved.
But this too can be achieved without using getDerivedStateFromProps. Using PureComponent can give you this same result with much less fuss. Another option can be shouldComponentUpdate.
In short, unless you have some very specific reason that getDerivedStateFromProps is right for your use case, you may be better off reaching for a solution that involves less hair pulling.

(React) Run method on props change, including initial props

I'd like to run a function upon receiving props initially and upon any subsequent props changes. Consequently, I was planning on checking props and running this function in both componentDidMount and componentWillReceiveProps. However, this seems redundant. I've seen people also check props and run functions inside render(), but I was under the impression this was frowned upon.
What should be my solution here?
There is no substitute to catch both componentDidMount and componentWillReceiveProps in a single callback. The best way to do this is to define a function and call it from both of these callbacks.
I was planning on checking props and running this function
Is a very vague description of what you're trying to do. Just checking the props alone doesn't seem to be problematic if done inside the render function. But it would become a problem if this part is causing side effects which (can) in turn trigger a component re-render. If this is the case, I'd say "frowned upon" is putting it lightly. The problem with this is that your render function is adding side effects which are in turn triggering more re-renders and on it goes. It will instantly kill the performance of your app and could even make the whole thing grind to a halt.
Your question is tagged with both React and Redux. Are you using a mapStateToProps function that you use with connect?
If so, a possible answer could be to dispatch this action (or run this function) in the mapStateToProps function, since this will be run both initially, and then every time you update the redux state. You'd have to make sure the action/function doesn't cause yet another state change though, so that you end up in a loop.
Edit: #DuncanThacker pointed out this might not be a good idea, because mapStateToProps may fire very often, resulting in performance issues. Good point.

Categories