React component not rerendering when props changes - javascript

Here are my 2 React components - Parent and Child
class ParentComponent extends Component{
render(){
/* consultations comes from redux store
and looks like { consultation_id:123, date:10/12/2013 } */
return(
_.map(this.props.consultations,(consultation)=>{
return{
<ChildComponent consultation={consultation} />
}
})
);
}
}
class ChildComponent extends Component{
render(){
return(
<div>this.props.consultation.date</div>
);
}
}
My problem is that I have certain actions that modify the consultations object. For eg: the props.consultation.date changes in the parent component, but the child component does not re render to show the latest consultation date.
However, I noticed that if I send each item in the consultations object to the child component like <ChildComponent date={this.props.consultation.date} /> it rerenders when the date changes!
Any idea why React does not re-render components when props sent as an object change?
I could always do with the work around but wondering if this is a bug or am I missing something?

The solution is to change consultation={consultation} to consultation={...consultation}. I am still unsure why, but it works!

You should definetly add the key prop with consultation_id to the child element. React can have problems rerendering elements without a key prop!

When you map over an array of items, you need to pass a unique key prop to each item. This signals to react which element is which.
...
_.map(this.props.consultations, consultation => {
return (
<ChildComponent
key={consultation.id}
consultation={consultation}
/>
)
})

The details you specified says that you change the date of same object~consultation in parent component and you parent component is getting the data as props.
Now if you mutating the same consultation object it won't make component re-render.
consultation.date = /* some other date */;
it'll not re-render the component.
But if you change the reference of the object like:
newConsulationData = { ...consultation }
newConsultationData.date = /* some other date */;
It'll work fine.
In your scenario you might have trouble as you directly mutating the props array object, and passing the same array so I suggest you change the reference of consultations array:
You'll need to
newConsulations = [ ...consultations ];
newConsultations[index of consultation].date = /* some other date */;
This should solve your problem.
When working with react try not to mutate the objects if you want to re-render the component on the changes .

I think it may be related to data type.
The first approach is passing an Object to child component,
<ChildComponent consultation={consultation} />
the second approach is passing a String to child component.
<ChildComponent date={this.props.consultation.date} />
When the property date of the object consultation is changed, the object's index is not changed. I think react is referencing an object by its index. Object returns an index not the exact value. The index is the specific address where object is stored in memory. But a string or boolean or number returns the value directly.
I think react is comparing object by index, and comparing string by the value. In the first approach, index does not update, so re-rendering is not happening.
The third approach extracts the object properties with spread symbol.
<ChildComponent consultation={...consultation} />
I think react is referencing the property itself in this approach, as each property is extracted. It's no longer referencing the object, so re-render works.
(I used a few I think statement here, as it's just my guess. Still need official documents to support.)

Related

Skip rendering children in React

Is there a way in React to skip over rendering children? I have existing DOM nodes on a page that I'd just like to wrap in a React component.
My use case is rendering the frame but not the static contents, which are pre-existing DOM nodes.
Something like:
const Frame = (props) => (
<div class="frame">
<SkipRender>{props.children}</SkipRender>
</div>
)
I'm guessing, when you say 'static children', you mean you don't want to rerender them? If so, then I have a solution for you.
React has a feature called memoization, which remembers a value and only updates it when the values it depends on (its 'dependencies') get updated. We can implement it through the useMemo() hook. A SkipRender component will look something like this:
function SkipRender(props) {
const children = useMemo(() => {
return props.children;
}, []);
return children;
}
What we're doing here is taking in the passed children elements, memoizing it (remembering its initial value), and keeping it static. The empty dependency array [] on the 4th line means children variable doesn't have any values it depends on, so it will not reevaluate its value. Since we're storing React elements to it, this will make it so those elements won't rerender after the initial render, even if the passed values change. Let me know if this helps, or if you need more clarification!

Spread only relevant props to component

I have a simple component and I want it to have most (if not all) the default HTML element props, maybe extending React.HTMLAttributes<HTMLElement> and then spreading them in the component's attributes.
But the props' type also includes some non-default to be used elsewhere, let's say props.cardTitle. That prop isn't useful for the main container, but will also be included in the final HTML. I could manually exclude it when spreading the props, but there may be a lot of them.
So the question is, how can I exclude all non-default props when spreading them in the main container?
Here's an example to further illustrate this:
type Props = ParentProps & OtherProps;
type ParentProps = {
className: string,
// onClick, etc. maybe extend all html element props.
}
type OtherProps = {
cardTitle: string,
cardBody: string,
cardPrice: string,
}
export default function ProductCard(props: Props) {
return (
// Pass all props excluding "OtherProps" that have no use here
// and inside use other props normally
<div {...props}>
<p>{props.cardTitle}</p>
<p>{props.cardBody}</p>
<p>{props.cardPrice}</p>
</div>
);
}
I've been trying some unconventional ways to do it. Probably using an object inside the type with all "non-default" props, and then just excluding that one, would be my best bet. But this still has some extra syntax to call it from another component.
Since I'm both new to React and moving from JS to TS, figured it would be better to ask.
You can use object destructuring to strip them out.
Example
let {cardTitle, cardBody, cardPrice, ...remainingProps} = props;

How to render React component taken from an array of objects and pass props to it?

I have an array of objects, one of the properties of each object is a JSX element that I want to render based on an index that I keep as a state in the parent component. I also need to pass props to these components, so I thought I could do something like <MyArray[index].myJsx foo="bar" /> But this seems not to be a valid syntax (due to the index part, I guess). How could I accoplish that? I don't think Array.map() fits my use case, since I want to show these components one at a time

Should you pass a whole object as a single prop or split it into smaller parts?

Assume I've got a component that is responsible for displaying a timeline item containing various data (id, label, type, timestamp, etc). When coming to render this component, what is the standard for the props?
Do I pass through the timelineItem object as a prop and deconstruct it within the component?
E.g.
({ timelineItem }) => {
const { id, label, timestamp, type } = timelineItem;
return ...
};
// Render
<TimelineItem timelineItem={timelineItemObject} />
Or alternatively, is it advised to split out the object outside of that component?
E.g.
({ id, label, timestamp, type }) => {
return ...
};
// Render
<TimelineItem id={id} label={label} timestamp={timestamp} type={type} />
I believe there can be performance benefits for splitting up the props into smaller chunks so that React can perform prop diffs better however take my code below. I've got two components that receive mostly the same props (As the group is mostly only responsible for displaying chunks of timeline items) and I'm unsure what structure to use for readability/maintainability
Thanks for any advice!
A props diff is performed by value on primitive types (number, string, boolean) and by reference on objects. So passing an object as props is faster than passing all of the properties of this object
As far as I understand, since ’timeline’ is being passed in and mapped for any change in timeline, the TimeLine items will be updated and rerendered anyway

React.cloneElement: pass new children or copy props.children?

I'm confused by the third "children" parameter of React.cloneElement and it's relation to this.props.children.
I followed this guide on higher order components and have the following code:
render() {
const elementsTree = super.render()
let myPropChange = {}
/* work on newProps... */
myPropChange.something = "nice!".
const newProps = Object.assign({}, elementsTree.props, myPropChange)
/* map children */
const newChildren = React.Children.map(elementsTree.props.children, c => something(c))
return React.cloneElement(elementsTree, newProps, newChildren)
}
Should I put the mapped children into my newProps.children or should I pass them as the third parameter to cloneElement?
Object.assign copied the children from props to newProps anyway, should I skip them?
In the guide it says
Components don’t have a guaranty of having the full children tree resolved.
What does that mean in my situation? That this.props.children is not there?
Added 4th question: Why should I clone the props at all and not just directly edit them?
Should I put the mapped children into my newProps.children or should I pass them as the third parameter to cloneElement?
Either should be fine.
Object.assign copied the children from props to newProps anyway, should I skip them?
When using cloneElement, you don't need to copy the props yourself. You can just do React.cloneElement(elementsTree, {something: 'nice!'}).
In the guide it says "Components don’t have a guaranty of having the full children tree resolved." What does that mean in my situation? That this.props.children is not there?
I can't be sure what that article meant, but I believe the author means that your component can choose not to use this.props.children. For example, if you render <Foo><Bar /></Foo>, then Foo will receive <Bar /> in this.props.children, but if Foo doesn't use this.props.children in its render method, then Bar will never be rendered.
Why should I clone the props at all and not just directly edit them?
React elements are immutable and there's no way you can change the props after an element is created. This allows some performance optimizations in React which wouldn't be possible otherwise.

Categories

Resources