React calling a method on load only once - reactjs

I'm new to Hooks and have been encountering some cases that have been sending me chasing my tail.
Hope someone can explain or provide solutions that make sense to me:
Loading a method only once on mount of a component is confusing. I tried this method, it works, but don't understand it. Can someone explain this to me:
const useMountEffect = fun => useEffect(fun, [])
useMountEffect(() => {
if (!isEdit) handleAddRow()
})
const handleAddRow = () => {
let appendArr = [...listArr, '']
setListArr(appendArr)
}
Following this thread:
How to call loading function with React useEffect only once
I tried just using useEffect without dependency and eslint does not like that and they recommend adding a skip next line which seems kind of hacky:
// eslint-disable-next-line

If I'm correct you want something similar to componentDidMount life-cycle method.
The way to do that is
function MyComponent(props){
useEffect(()=>{
// do stuff here...
}, []) // <-- empty dependency array
return <div></div>
}
To understand what's happening you'll need to understand how the useEffect hooks work.
Using the useEffect hook without any dependencies will trigger the effect every time some state or prop changes and causes a re-render.
but if we pass an empty array as a dependency it will be as the effect not dependent on anything else, so it will only trigger when the component mounts.

The empty array [] argument tells useEffect() to run only once
It will run only once on mount, like componentDidMount used to
Explanation:
useEffect() takes 2 arguments - your function and an array of dependencies
useEffect(
yourFunction, // <- function that will run on every dependency update
[] // <-- empty dependency array
)
The dependency array argument tells it when to run your function. If the dependencies don't change it won't run again. If dependencies do update it will run your function again.
If you pass in an empty array (no dependencies) then the function will only run on mount.
ESLint missing dependency error
React Hook useEffect has a missing dependency: 'xxx'. Either include it or remove the dependency array react-hooks/exhaustive-deps
The reason for the ESLint rule is that useEffect() is designed to run on every dependency update it will expect you to add all the dependencies to that array.
To ignore this you can add above the dependency array line:
// eslint-disable-next-line react-hooks/exhaustive-deps

For anyone looking to execute a function once per whole App (globally), this could be a solution:
if (typeof window !== 'undefined') { // Check if we're running in the browser.
// ✅ Only runs once per app load
checkAuthToken();
loadDataFromLocalStorage();
}
function App() {
// ...
}
Source / extended explanation here.

Related

Resolving TypeScript React JS Lint: React Hook useEffect has a missing dependency without using comment to disable lint or without passing dependency

I am build a Web application using React JS and TypeScript. I am using React hooks. I created a custom hook and using that in a Component and calling a function of that hook inside that useEffect hook within the component as follow.
const Products: FC<{}> = () => {
const productsHook = useProducts();
useEffect(() => {
productsHook.fetchProducts();
}, []);
When I run lint, I am getting the following warning.
React Hook useEffect has a missing dependency: 'productsHook'. Either include it or remove the dependency array react-hooks/exhaustive-deps
When I add the following line,
// eslint-disable-next-line react-hooks/exhaustive-deps
right after this line, productsHook.fetchProducts();, it resolves the warning.
The other way to resolve the issue it to add productsHook.fetchProducts as an array element in the second parameter of useEffect like this.
useEffect(() => {
productsHook.fetchProducts();
}, [productsHook.fetchProducts]);
But, technically, it is unnecessary to do that. It will work perfectly fine if we don't do that too. Is there a way to resolve that warning without the comment to ignore or without passing the dependency in the second parameter of useEffect?
I would just do it the second way, adding the function to the dependency array. It is for instance what is recommended to do when using dispatch in Redux, even though the dispatch function is guaranteed to always be the same, eslint cannot detect that, and the easiest way to solve it is to add it to the dependency array.
It's definitely a better option than getting into the habit of disabling warnings that you don't like.

useEffect as dependency array or trigger array?

I am having trouble knowing how the useEffect dependency array rules work.
By the docs I see that it tells me, we should include all the props/state/derived vars that are used inside the useEffect.
So let's say we want to execute some code when the param count changes, but access the innerCount value as well. By the docs it states I should add it to the dependencies list to prevent the code from having stale values from previous renders, but technically it will always get the correct value even if I don't pass it in the dependency list...
And also if I do add it to the dependency list, that means I would have to do double validation inside the useEffect just to run that code ONLY when count has changed from its previous value...
function Component({ count }) {
const [innerCount, setInnerCount] = useState(0);
useEffect(() => {
console.log('innerCount', innerCount);
setInnerCount(innerCount + 1);
}, [count]);
return <div>
<span>Count: {count}</span>
<span>innerCount: {innerCount}</span>
</div>;
}
My point is, should I follow react docs and always include all dependencies of the useEffect which adds a lot of complexity? or just ignore it for these cases when stale values will not happen?
You should always include it, but I know it can be annoying sometimes.

React hooks appends unwanted value with eslint-plugin-react-hooks#next

I have a useEffect hook in my component which call a Redux action to fetch some data.
useEffect(
() => {
props.getClientSummaryAction();
},[]
);
When I go to save the file the linter does this.
useEffect(
() => {
props.getClientSummaryAction();
}, [props]
);
Which obviously send my component into an infinite loop as getClientSummaryAction fetches some data which updates the props.
I have used deconstruction like this and the linter updates the array.
const { getClientSummaryAction } = props;
useEffect(
() => {
getClientSummaryAction();
},
[getClientSummaryAction]
);
Which I am fine with but it doesn't really make sense because getClientSummaryAction will obviously never be updated, its a function.
I just want to know why the linter is doing this and whether this is best practice.
It's not unwanted. You know for a fact that the dependency is not going to change, but React can possibly know that. The rule is pretty clear:
Either pass an array containing all dependencies or don't pass anything to the second argument and let React keep track of changes.
Pass the second argument to useEffect is to tell React that you are in charge of hook's call. So when you don't pass a dependency that is included inside your effect eslint will see this as a possible memory leak and will warn you. DO NOT disable the rule just continue to pass the dependency as you are already doing. May feel redundant but it's for the best.

How to fix a react-hooks/exhaustive-deps linting error with no dependency

I have a linting error (react-hooks/exhaustive-deps) that I am currently disabling on two lines. I understand how the code is breaking the rule, but I do not understand why this rule applies to this situation.
I want an effect to only run for cleanUp purposes when the component unmounts. I have a function, clearMessages, which is inherited from props which reset the state of a reducer back to its empty default state. It works fine when I run my project locally, but when I run the build react-scripts throws the above linting error and the build fails.
Here is a short snippet showing the effect that causes the problem.
const Search = ({ clearMessages }) => {
useEffect(() => () => clearMessages(), [])
...
...
}
This is the error message that react-scripts build throws.
Line 25: React Hook useEffect has a missing dependency:
'clearMessages'. Either include it or remove the dependency array. If
'clearMessages' changes too often, find the parent component that
defines it and wrap that definition in useCallback
react-hooks/exhaustive-deps
I would not expect clearMessages to change, so I am not sure why it is important that I provide it as a dependency. I do not want the effect to the only run when the value of clearMessages changes.
Along with the above answer, I found this documentation to be very helpful for reorganizing my useEffect when there is a functional dependency:
https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies
If clearMessages does not change, it is equivalent to an empty dependency array - [].
Either way, because "I would not expect clearMessages to change" you should use it in the dep array like the linter suggests.
const Search = ({ clearMessages }) => {
// Equivalent to an empty dependency array !if! clearMessages never change.
useEffect(() => () => clearMessages(), [clearMessages])
There are two phases to useEffect mentioned above:
useEffect will run when clearMessages changes (if it doesn't it will run only on the component mount).
By specifying a return callback, the callback will run on component unmount.
Quote from docs: When exactly does React clean up an effect? React performs the cleanup when the component unmounts.

React hooks: how to access props within "mount" useEffect & not throw linting warning

Unless I'm mistaken, this is valid code:
useEffect(() => {
if (prop1) {
doSomething();
}
}, []);
(prop1 is a prop). But when linting I get the following error:
React Hook useEffect has a missing dependency: 'prop1'. Either include it or remove the dependency array.
(react-hooks/exhaustive-deps)
I don't want to pass prop1 as a dependency because I would lose the "only run on mount" behaviour. But I need to access the prop to doSomething().
Any suggestions?
Hooks were new when this question was written, so maybe you already know this, but in case you or someone else wants to know:
React thinks that because your effect uses the value of prop1, it "depends" on prop1 and should be re-run whenever it changes. That's why the linter is complaining that it's not listed as a dependency.
However because you want the effect to only run "on mount", you want it to use the value of prop1 from the initial/first render, and never run again even if prop1 changes. This is at odds with the conceptual idea that the array lists all the variables the effect depends on, which is what the linter is focused on.
The solution alluded to in the React Hooks FAQ is to use useRef to keep track of whether or not this is the first render (edited):
const firstRenderRef = useRef(true)
useEffect(() => {
if (firstRenderRef.current) {
firstRenderRef.current = false
doSomething(prop1)
}
}, [prop1])
This solution satisfies the linter, specifically because it follows the idea of listing all dependencies of the effect in the array. The ref allows the effect to also depend on a variable for whether this is the first render or not, but without rerendering when the value changes.
You could probably raise this here.. [ESLint] Feedback for 'exhaustive-deps' lint rule
Though I get the feeling where this is a case where you should add an eslint "ignore" comment if you are sure you don't want the effect to run on update of prop1.
A legit case for relaxing the warning was raised here..
https://github.com/facebook/react/issues/14920#issuecomment-467896512
Also check what version of the plugin you are running..
https://github.com/facebook/react/issues/14920#issuecomment-468801699
try this code
const usemount = ( functionToDoSomeThing, data ) => {
useEffect( () => {
functionToDoSomeThing( data );
},[] );
};
usemount( console.log, props );
i define a function to do Something and pass it to hook
in example i use console.log function

Resources