In react, specifically referencing the hooks/functional paradigm, when do you need to use props.functionName? As far as I can tell, if the functions are named the same, you can omit props as follows:
Parent.js
...
<Child functionName={functionName}/>
...
Child.js
...
functionName();
...
However, if the name changes, props must be referenced as follows:
Parent.js
...
<Child otherName={functionName}/>
...
Child.js
...
props.otherName();
...
Am I correct? If so, why use the second design pattern? (Perhaps just to call out that the function comes from a parent and isn't defined at the child level. Or maybe some other reason?)
No. The name you use depends entirely on how the child component processes the props it is passed. How the parent component determines what value to pass is completely irrelevant.
If the props are placed in a variable named props then you need to access them through the object stored in the variable.
const Child = (props) => {
props.functionName();
...
}
If the first argument is destructured, then each property is stored in its own variable.
const Child = ({functionName}) => {
functionName();
...
}
If the value is copied to another variable inside the component then you can also use the variable it was copied to.
const Child = (props) => {
const functionName = props.functionName();
functionName();
...
}
False, It is Destructuring Props in React;
Destructuring is a convenient way of extracting multiple values from data stored in (possibly nested) objects and Arrays
props is an object so we can use the Destructuring
Destructuring gives access to the use of props in a more readable format and discards the need for props for every property.
<Child functionName={functionName} name={name}/>;
const child = (props)=>{
// you need to write props.functionName
props.functionName();
const thisName = props.name
};
const child =({functionName,name})=>{
// you dont need to write *props.*
functionName();
const thisName = name
};
When interfacing with a child component, the name that you use for something in your parent component may be different from the prop that the child component requires.
Just for example, say that your parent component has something to do with school, and you have:
const [studentId, setStudentId] = useState();
const [parentId, setParentId] = useState();
But you also have a child component for students that expects an id prop. Then, to pass down the studentId to it, you'd do:
<Student id={studentId} />
And in that child component, you'd reference it by doing props.id.
You wouldn't want to do
const [id, setId] = useState();
in the parent component, and then
<Student id={id} />
when passing it down because then it wouldn't be clear in the parent component which ID that stateful value referred to - you'd probably have to add a comment.
If so, why use the second design pattern?
Sometimes, like in the situation I just described, the child component isn't designed with all identifiers used in the parent component in mind - which is perfectly reasonable, that's what allows for so many things in programming to be modular (and adaptable and useful). As a result, sometimes you need a prop or variable to have one name in the parent component, and another in the child component, which requires the
<Child otherName={functionName}/>
approach.
Related
Hello i have an array called info[] in a grandchild component and i want my parent component when a button is clicked to access the array. I also want a sibling component to have access to it. How is this possible .. i am a bit confused.
Should I use use-context ?
Thank you!
If I have understand what you are asking it could be something like this.
const GrandChild = ({ setParentInfo }) => {
const info = [1, 2, 3];
const handleClick = () => {
setParentInfo(info);
};
return <button onClick={handleClick}>Set parent info</button>;
};
const Sibling = ({ parentInfo }) => {
return <div>{parentInfo.length}</div>; // Do whatever you need with parentInfo
};
const Parent = () => {
const [parentInfo, setParentInfo] = useState([]);
return (
<div>
<GrandChild setParentInfo={setParentInfo} />
<Sibling parentInfo={parentInfo} />
</div>
);
};
Here you don't need context because you don't have that much layers but if you need to drill down the props than use a context.
If you want to share state among many different components in your application, and you believe that passing State as a prop is "a very long journey" to move around I'm probably you should consider something like use context hook.
Either way what you just described seems like a simple use case witch will not need context.
What you should do is:
On the parent you have [state, setState]
On the current component pass setStat as a prop to child component and then from child component pass setState as a prop to grandchild component.
Then on grandchild component you can do something like:
props.setState(array).
So now on the parent component the variable state will have been updated with the value array from the grandchild component.
If you want to pass state to siblings component, and by sibling I assume you mean sibling of the parent,
Then you should move the state from parent one level up let's say the parent of the parent.. and do what I've described above.
So create useState high in your component tree,
And pass State and setState as well to children as props, setState will be passed as function, so you can call it on the grandchild component or any other component
Say we got a Page-component that delegates the rendering of notifications to a Notification-component. The Page-component's render method contains the following ...
{this.state.notifications &&
<Notifications
notifications={this.state.notifications}
removeNotifAt={index => this.setState(prevState => {
const copy = [...prevState.notifications]
copy.splice(index, 1)
return { notifications: copy }
})}
removeNotifyBy={id => this.setState(prevState => {
const copy = [...prevState.notifications]
const index = copy.findIndex((notif, _) => { return notif.id === id })
copy.splice(index, 1)
return { notifications: copy }
})}
/>
}
... as you might notice, Notifications require some rather large function to alter the state of its parent. Since they access this.state, these functions have to be defined in the parent of Notifications, in this case Page.
Now, one can imagine that multiple pages have notifications that needs rendering and so they all have to code-duplicate the code snippet above. As we all know, code-duplication is bad, so how can we best avoid it?
It's impossible to extract the functions removeNotifyAt and removeNotifyBy out into functions defined in, say, Notifications.js since they need to access this.state.
So, what's the react-way of dealing with such duplicate functions that you can't extract away because it needs to access this.state? I suppose I am not the first one stumpling upon this, giving how trivial of a case this is.
You can move the code from removeNotifyBy and removeNotifyBy into functions, and place them in the parent component. Then you can pass them into the child components as props.
You could extract those functions into Notifications.js. (That is in case you don't wish to write those functions inside parent component).
Both the parent's state as will as the function that sets the state can be passed as props to Notifications component.
Example:
<Notifications parentState={this.state} parentStateHandler={this.setState} />
//Note: Ideally props shouldn't be named like this and one must avoid passing entire state object as prop, rather you should split it into props that the child component requires. However this is just to give you the clarity regarding how to access parent's state in child
Now Notifications component has entire parents state which can be accessed by props.parentState and you can also set Parent state by using props.parentStateHandler instead of this.setState
In terms of writing components, which would be the preferred way to write below component? Assume that removeCard is outside of shown scope, ie. redux action.
My assumption would be that ComponentCardB would be, as it avoids passing an unnecessary argument which would be in the scope anyway. I imagine in terms of performance in the grand scheme of things, the difference is negligible, just more of a query in regards to best practise.
TIA
const ComponentCardA = (id) => {
const handleRemove = (cardId) => {
removeCard(cardId);
};
<div onClick={() => handleRemove(id)} />;
};
const ComponentCardB = (id) => {
const handleRemove = () => {
removeCard(id);
};
<div onClick={handleRemove} />;
};
With functional components like that, yes, there's no reason for the extra layer of indirection in ComponentCardA vs ComponentCardB.
Slightly tangential, but related: Depending on what you're passing handleRemove to and whether your component has other props or state, you may want to memoize handleRemove via useCallback or useMemo. The reason is that if other props or state change, your component function will get called again and (with your existing code) will create a new handleRemove function and pass that to the child. That means that the child has to be updated or re-rendered. If the change was unrelated to id, that update/rerender is unnecessary.
But if the component just has id and no other props, there's no point, and if it's just passing it to an HTML element (as opposed to React component), there's also probably no point as updating that element's click handler is a very efficient operation.
The second option is better way because using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.
Also if you don't want to use syntax with props.id you rather create function component with object as parameter:
const Component = ({id}) => { /* ... */ }
Of course using arrow function is also allowed but remember, when you don't have to use them then don't.
I have a project where I'm displaying cards that contain attributes of a person in a textfield, and the user can edit the textfield to directly change that person's attribute values. However every time they're editing the textfield it causes a rerender of all cards which slows down the app. Here is an example:
export default Parent() {
const [personList, setPersonList] = useState(/* list of person objects*/);
const modifyPerson(index, property, value) {
const newPersonList = _.cloneDeep(personList);
newPersonList[index][property] = value;
setPersonList(newPersonList);
}
const children = personList.map((person, index) => {
<Person
modifyPerson={modifyPerson}
index=index
/*properties on the person */
/>
});
return <div> {children} </div>
}
export default Person(props) {
const fields = /* assume a list of these textfields for each property */
<TextField
value={props.name}
onChange={(e) => modifyPerson(props.index,"name",e.target.value)}
value={props.name} >
return {fields};
}
So essentially when the child's text field is updated, it triggers a state change in the parent that stores the new value, then refreshes what the Child looks like. There's no button that the user clicks to "save" the values-- as soon as they edit the textfield it's a permanent change. And also the parent needs to know the new values of every Person because there's some functions that require knowledge of the current state of the person list. The Person component contains images that slow down the rendering if done inefficiently.
Is there a better way to make this design more performant and reduce rerenders? I attempted to use useCallback to preserve the functions but I don't it works in this specific design because the property and values are different-- do I have to create a new "modifyPerson" for each exact attribute?
Use React.memo()
React.Memo will check the props passed to the component and only if the props changes , it will re-render that particular component.
So, if you have multiple Person component which are getting props which are explicit to those Person, and when you change a particular Person component which leads to Parent state getting updated, only that Person component which you had modified will re-render because its props will change(assuming that you pass the changed value to it).
The other components will have the same props and wont change.
export default React.memo(Person(props) {
const fields = /* assume a list of these textfields for each property */
<TextField
value={props.name}
onChange={(e) => modifyPerson(props.index,"name",e.target.value)}
value={props.name} >
return {fields};
})
As others have already said React.memo() is the way to go here, but this will still re-render because you recreate modifyPerson every time. You can use useCallback to always get the same identity of the function, but with your current implementation you would have to add personList as dependency so that doesn't work either.
There is a trick with setPersonList that it also accepts a function that takes the current state and returns the next state. That way modifyPerson doesn't depend on any outer scope (expect for setPersonList which is guaranteed to always have the same identity by react) and needs to be created only once.
const modifyPerson = useCallback((index, property, value) {
setPersonList(currentPersonList => {
const newPersonList = _.cloneDeep(currentPersonList);
newPersonList[index][property] = value;
return newPersonList;
})
}, []);
I'm new in react hooks and I just don't see this on docs:
const MyComponent = ({myProp}) => {
const [myPropHook, setPropHook] = useState(myProp)
...
}
I'm wondering if this is a good practice?
The value you pass to useState is used as a starting value for the state variable. So, when your component props change, they will not affect the state variable you are using. The initial value would be the first props sent to the component and after that can be modified only using the setPropHook function.
So, in short, it is definitely a code smell to use props as initializers for useState because reading the code does not correctly convey what will actually happen.
You don't see it much because it doesn't make a lot of sense in terms of how a React app should distribute its state.
If a prop value is set higher up the tree, it shouldn't be used as part of the separate state within a component. It makes sense to use prop values to determine the state of a component indirectly as in 'if the prop is this, then set the state to that', but not to directly copy the prop in to the initial value.
In other words, the internal state of a component (accessed via the useState and useReducer Hooks) should be determined by the component, not directly by the parent(s).
Yes, this is bad. What you're doing is passing a prop to the state, and it is discouraged by many.
The React docs says that "using props to generate state often leads to duplication of “source of truth”, i.e. where the real data is.". The danger is that if the props is changed without the component being refreshed, the new prop value will never be displayed, because the initialization of state from props only runs when the component is first created.
The only exception would be to use the prop as a seed for an internally-controlled state. After several years of react development, I've never encountered such a case.
Further reading:
React component initialize state from props (SO question)
React Anti-Patterns: Props in Initial State (medium.com article)
Why Setting Props as State in React.js is Blasphemy (blog post)
If you are trying to receive a prop to that functional component, then yes, but not exactly like you have it written. So in the parent component you will have something like this:
const App = () => {
const [resource, setResource] = useState("posts");
and then there is a component inside the JSX like so:
const App = () => {
const [resource, setResource] = useState("posts");
return (
<div>
<div>
<button onClick={() => setResource("posts")}>Posts</button>
<button onClick={() => setResource("todos")}>Todos</button>
</div>
<ResourceList resource={resource} />
</div>
);
};
That ResourceList component has to be able to receive the props that the App component is passing to it. Inside a class-based component you would do {this.props.resource}, but in our case, where its a functional component using React hooks you want to write it like so:
const ResourceList = (props) => {
const [resources, setResources] = useState([]);
or via ES6 destructuring like so:
const ResourceList = ({ resource }) => {
const [resources, setResources] = useState([]);