Passing a hook as a prop - javascript

Is it acceptable / common to pass in a React Hook as a prop to another Component?
Or are they meant to only belong within the component it is declared in?
The following is just an example to illustrate what I mean by passing it as
a prop from the Parent Component to the Child parent.
import React, { useState } from 'react';
export const Parent = () => {
const [count, setCount] = useState(0);
return <div>
Current count is {count}
<Child setCount={setCount} /> // passing a hook set call as a prop to child
</div>
}
const Child = ({setCount}) => {
setCount(10) // using the hook set call
return (
<div>
Some Child Text
</div>
);
};
export default Child;

setCount is not a hook, it is just another function, So you can pass it as a prop. You can also think of the output of useMemo or useCallback, these can be passed as a prop.
useState is a hook, useCallback is a hook, or even for a matter any function, which encapsulates other hooks, these should not be passed as prop.
Why?
To answer this first think of why would you want to pass the hook as a prop? The benefit of passing the hook as a prop will be that you can conditionally pass another hook / skip the call.
But in the case of hooks, It should not be called conditionally. So there is no point. of passing it as a prop, Just import it and use it.

Your example is totally fine and can be used. setCount is just a function. From your example, the state belongs to the parent component. However, its value will come from another component API.
The same would happen if you change the child component in your example for a regular <button onClick={} />.
I know that this example probably doesn't match your real case implementation. I'd only suggest you pay attention to how the child component API is designed in order to make the parent not tightly coupled to the child.

Related

Status tracking and props in hooks - How does React keeps (track) state and props in Hooks?

I know that in React class components React uses the "this" binding to "track" props and state. In hooks it is achieved thanks to closures, is my doubt?
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
This code works as you would expect a counter, if you were using class component you would have to use setState() in callback mode to access the previous state, but in hooks how does the magic happen?
are there any bindings on the hooks or not?
React keeps track of state by internally counting the number of times useState is called the first time a component mounts. The value passed into useState is then set into React's internal state. For example, given
const Component = () => {
const [state1, setState1] = useState('one');
const [state2, setState2] = useState('two');
return 'foo';
};
React will then have the following state stored internally:
['one', 'two']
with one item in the (hidden, internal) state array for each call of useState that there is.
When a state setter is called, the corresponding item in the internal state array will be updated, and then the component will re-render. When a component re-renders, each useState call then returns the appropriate item in the internal state array. The first useState call will return the first item in the internal state array, the second useState call will return the second item, and so on.
In your code, with hooks, the click handler has a closure over the value of count that existed at that particular render of Counter.
if you were using class component you would have to use setState() in callback mode to access the previous state
Not really - if this code was a class component, it would be free to reference this.state.count directly without using the callback version.

Why can I not use an inline function for a Screen component?

In the getting started of the React-Navigation documentation they say:
Note: The component prop accepts component, not a render function. Don't pass an inline function (e.g. component={() => }), or your component will unmount and remount losing all state when the parent component re-renders. See Passing additional props for alternatives.
From my understanding if I write:
const WrappedHome = () => <HomeScreen />;
Then later use this:
<Stack.Screen name='home' component={WrappedHome} />
That would be equivilent to their example. Is React or JSX or React-native doing something special to cause inline function in props to be evaluated differently than standard Javascript? Or what am i misunderstanding?
A component is not just a regular function. When you render a component, it has a "type" - which is the function that creates the component:
e.g:
function MyComponent() {
// whatever
}
<MyComponent /> // the component type is MyComponent
Whenever a re-render happens, React looks at this type. If it's the same, then it'll keep the component's state, and re-render it.
Now let's say you render MyComponent during first render, but then changed it to OtherComponent later. React will unmount MyComponent and render OtherComponent. All the local state of MyComponent will be destroyed when it unmounts.
When you create components inline inside another component, every render, there's a new function created, which means a new component type every render. Since the type changes every render, React will unmount previous component and mount the new component every time, which is undesirable.
Take the following example:
const a = { foo: 42 };
function getA() {
return a;
}
getA() === getA(); // the returned `a` is always the same because you defined the object outside `getA`
function getB() {
return { foo: 42 };
}
getB() !== getB(); // the returned object is a new one every time you call `getB` because you defined the object inside `getB`
So it's not that inline functions are evaluated differently, the problem is because every time there's a new component which destroys any local state and even if you don't have local state, unmounting a component and mounting a new one is usually slower than updating the same component.

Can I declare a variable outside of useEffect ? React Hooks

I'm learning React Hooks, and I'm actually using the useEffect method.
No problem at all, but I got some warnings about my variable declarations.
Here's an example of what I wrote :
import React, { useRef, useEffect } from 'react';
function App(){
let containerRef = useRef(null);
let myVariable;
useEffect(){
myVariable = containerRef.children[0];
}
return(
<div className="container" ref={el => containerRef = el}>
<h1>Hey, I'm Laurie </h1>
<p> Nice to e-meet you!</p>
</div>
)
}
This is just an easy and uncompleted example of what I did, for animating my website with GSAP. I access to the DOM elements with useRef and I only found this solution.
But my console write me some warnings, and I'm pretty lost.
Warning I got :
Assignments to the myVariable variable from inside React Hook useEffect will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside useEffect.
Can someone help me with this issue ?
when you are using Hook basically you are telling React that your component needs to do something after render. React will remember the function you passed, and call it later after performing the DOM updates.
More Details
https://reactjs.org/docs/hooks-effect.html#example-using-hooks
Regarding warning: React doesn't remember your myVariable. It will be recreated on each render. It will be better to use useState hook to set value in useEffect and remember it on the next render.
Remember one thing always pass the second argument in useEffect with an array of dependencies.

Why doesn't useState function initialize state every time?

import React, { useState } from "react";
function HookCounter() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count {count}</button>
</div>
);
}
export default HookCounter;
React calls this function every time when it needs to re-render.
But why doesn't it initialize the state every time?
When exit the function, life of variables is ended, isn't it?
But how does it keep saving values of states?
I don't understand.
In useState function, is there any logic for that?
useState as a function is storing the value you gave it within React's core. When the component re-renders, React is going to pass the updated count from its core to the component.
More information here.
State is initialized only once when you create the component, this is how React works. From the documentation:
What does calling useState do? It declares a “state variable”. Normally, variables “disappear” when the function exits but state variables are preserved by React.
Just to have the context here, let me summarize what is useState and how it works in more details.
What is useState:
So useState is a hook which helps you to handle state in a functional component.
How is it working?
Once you call useState you need to pass the initial value of the state what you would like to use in your functional component. It returns a pair of values the count and setCount.
So let's have your example below:
const [count, setCount] = useState(0);
So useState returned two items where count is the current value. And the second item, setCount is a function which can be used to update the value of the count state.
count can be used to represent the state for example the value in a div element:
return (
<div>{count}</div>
)
In order to update the value you can achieve by calling setState(12).
From the docs you can read further here.
According to the React official website:
React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).
There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like useState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple useState() calls each get independent local state.
Reference:
How does React associate Hook calls with components?
Why doesn't useState function initialize State every time?
I think you're confused with:
const [count, setCount] = useState(0);
As it's just a variable!?!
Yes, it's just a variable but it will take everytime the function runs from the useState hook. And it is the local state like you have seen in class based component.

Prevent re-render when sending function prop from functional component

When sending props to a PureComponent or a functional component, you can optimize performance by using props that don't change for every render, which will prevent the component from re-rendering.
When using class components this is simple:
class Component extends React.Component {
render() {
return <List createRows={this.createRows}/>;
}
createRows = () => this.props.data.map(dataToRow);
}
Given List being either a PureCompoment or a functional component, the createRows prop will never cause a re-render of List.
But if the Component is a functional component, this is no longer possible:
function Component(props) {
return <List createRows={createRows}/>;
function createRows() {
return props.data.map(dataToRow);
}
}
Since createRows is created every time Component renders, the prop will change, causing a re-render of List every time Component is re-rendered. This can cause a big loss in performance. Notice also that the createRows cannot be placed outside the functional component, since it is dependent on the data prop of List.
Now, with the introduction on Hooks, it is possible to hold the createRows in a useState hook:
function Component(props) {
const [ createRows ] = useState(() => () =>
props.data.map(dataToRow);
);
return <List createRows={createRows}/>;
}
Since the createRows is saved in a state hook, it will not change with each render, and no re-render of List will occour, like we want.
However, this seems more like a hack than a solution.
What is best practice for sending a function prop from a functional components to a child component, without causing unnecessary re-renders of the child component?
useCallback hook exists exactly to solve this problem. I advise you to carefully read the official guide to hooks, it pretty much answers all possible questions
function Component(props) {
const createRows = useCallback(() =>
props.data.map(dataToRow);
), []); // provide dependencies here
return <List createRows={createRows}/>;
}
This is the purpose of useCallback. You can find more details in some of my related answers below.
Trouble with simple example of React Hooks useCallback
What is the intension of using React's useCallback hook in place of useEffect?
React Hooks useCallback causes child to re-render

Categories

Resources