granchild rendering when its prop not change - javascript

I'm using react functional component in order to render 3 levels of component.
The child is depent on props being passed by the grandparent.
in my exanple below,, Child2 is keep rendering even only props.child1 is being updated.
is there any way to avoid rendering of Child2 in case props.child2 value never changed? what is the best practice for this kind of scenario?
I'm not using redux, onlu hooks.
const Prandparent = () => {
return (
<Parent child1={props.child1}
child2={props.child2} />
)
};
const Parent = () => {
return (
<div>
<Child1 reportElasticLog={props.child1} />
<Child2 reportElasticLog={props.child2} />
</div>
);
};
Thanks,

You need to add React.memo to those components https://reactjs.org/docs/react-api.html#reactmemo
Be careful and add React.memo to the entire subtree of components, following the note from PureComponent (which is similar): https://reactjs.org/docs/react-api.html#reactpurecomponent
Furthermore, React.PureComponent’s shouldComponentUpdate() skips prop
updates for the whole component subtree. Make sure all the children
components are also “pure”.

Related

Passing Hook down to grandchild/multiple components

I'm wondering If I can pass a hook down multiple components. For example in my parent component I declare a hook and I pass the hook components into the child. And within that child I pass that hook into the grandchild and within the grandchild I set the state for the hook. I know that I can pass hooks from parent to child but unsure if I can do it for multiple components.
export const Parent = () => {
const [data, setData] = useState("")
return(
<Child data={data}, setData={setData}
)
export const Child = ({data,setData}) =>
return(
<GrandChild data={data}, setData={setData}
)
export const GrandChild = ({data,setData}) =>
setData("hi")
return(
<div>{data}</div>
)
You can do this as many times as you want but there are better ways to do this like Redux or Context API. These tools are for preventing these kind of redundant actions.
This can get messy very quickly as apps grow and can cause issues finding bugs and errors. It is known as prop drilling. I would strongly recommend the following documentation on
useContext

How can I prevent rerendering of a pure component with pure children in React?

I have a functional React component in which I use React.memo to prevent unnecessary rerenders. If it contains only a string as a child, it is only rendered only when the string is changed (as expected). However, When I add another child component which also uses React.memo, it is always rerendered (even when child component is not rerendered). Here is a sample code indicating those cases:
const Parent = React.memo(() => <div> Parent </div>) // Rendered only once!
Second Case:
const Child = React.memo(() => <div> Child </div>); // Rendered only once!
const Parent = React.memo(() => <Child />); // Rerendered everytime
To be more clear, the Parent is always rerendered (as if React.memo is not used) in the second case. Obviously, there is no need to rerender it as its child does not rerender and thus none of Parent's children is changed. Is there something I am missing? How can I change my implementation to avoid rerendering of Parent component?

Why are all the PureComponent children of a container updating when only one child's state is modified in the container's state tree?

Learning React and Redux. I'm playing with the Redux examples, and currently looking at the todo-with-undo example (don't think its possible to set up a sandbox or something). In this setup there is a container component (TodoList) and it's children (Todo).
I modified the Todo from a functional component to PureComponent class so that shouldComponentUpdate() returns false if all the prop references are the same, and thus the components shouldn't update (but they will still re-render???).
Adding code to log when a child updates with componentDidUpdate() method and also to log when re-rendering shows that every time a new Todo is added to the container, all elements update and rerender - even whilst being PureComponents - it should be the case that shallow comparisons of old and new props for each child should return false for the new or updated child.
Being an example built by Redux I doubt that they update the store incorrectly (not in an immutable fashion), as that is the point of their concept, so I believe I am not fully understanding something -> someone please help...
Because the <TodoList> is passing a new callback function reference to each child:
export default class TodoList extends Component {
render() {
return (
<ul>
{this.props.todos.map((todo, index) =>
<Todo {...todo}
key={index}
onClick={() => this.props.onTodoClick(index)} />
)}
</ul>
);
}
}
That will always cause the child to re-render, even if it's attempting to optimize renders based on props comparisons.
PureComponent does not mean it will not update, it just mean React will handle comparison of props and state for you by implementing shouldComponentUpdate with shallow prop and state comparison.

React.useMemo does not work to prevent re-renders

I have the following simple component:
const Dashboard = () => {
const [{ data, loading, hasError, errors }] = useApiCall(true)
if (hasError) {
return null
}
return (
<Fragment>
<ActivityFeedTitle>
<ActivityFeed data={data} isLoading={loading} />
</Fragment>
)
}
export default Dashboard
I would like to prevent ALL re-renders of the ActivityFeedTitle component, so that it only renders once, on load. My understanding is that I should be able to use the React.useMemo hook with an empty dependencies array to achieve this. I changed by return to be:
return (
<Fragment>
{React.useMemo(() => <ActivityFeedTitle>, [])}
<ActivityFeed data={data} isLoading={loading} />
</Fragment>
)
As far as I'm concerned, this should prevent all re-renders of that component? However, the ActivityFeedTitle component still re-renders on every render of the Dashboard component.
What am I missing?
EDIT:
Using React.memo still causes the same issue. I tried memoizing my ActivityFeedTitle component as follows:
const Memo = React.memo(() => (
<ActivityFeedTitle />
))
And then used it like this in my return:
return (
<Fragment>
{<Memo />}
<ActivityFeed data={data} isLoading={loading} />
</Fragment>
)
Same problem occurs. I also tried passing in () => false the following as the second argument of React.memo, but that also didn't work.
Use React.memo() instead to memoized components based on props.
React.memo(function ActivityFeedTitle(props) {
return <span>{props.title}</span>
})
Take note:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
The second argument passed to React.memo would need to return true in order to prevent a re-render. Rather than computing whether the component should update, it's determining whether the props being passed are equal.
Your usage of useMemo is incorrect.
From react hooks doc:
Pass a “create” function and an array of dependencies. useMemo will
only recompute the memoized value when one of the dependencies has
changed. This optimization helps to avoid expensive calculations on
every render.
If no array is provided, a new value will be computed on every render.
You need to use useMemo like useEffect here for computation of value rather than rendering the component.
React.memo
React.memo() is the one you are looking for. It prevents re-rendering unless the props change.
You can use React.memo and use it where your define your component not where you are making an instance of the component, You can do this in your ActivityFeedTitle component as
const ActivityFeedTitle = React.memo(() => {
return (
//your return
)
})
Hope it helps
It's because rendering a parent causes it's children to re-render for the most part. The better optimization here would be to place your data fetching logic either in the ActivityFeed component or into a HOC that you wrap ActivityFeed in.

Share data between React components with no relation?

I'm working on a React component library that allows for client-side data filtering by passing an array of objects and an <input/> as props to a <SearchFilter/> component. I want to return the filtered results to a separate <SearchResults/> component that can be rendered elsewhere in the tree (i.e. the results component doesn't have to be a child of the input component).
I've got the filtering figured out, but I'm not sure the best route to take in React on getting the filtered data to the <SearchResults/> component.
This is what I'd like to end up with...
<SearchFilter
data={data}
input={<input type="text" value={value} onChange={this.handleChange}/>}
/>
Then, using Render Props to return the data and map over that to return JSX, there would be the results component. Something like this...
<SearchResults
render={data => (
data.map(el => (
<div>
<span>{data.someProperty}</span>
</div>
)
)}
/>
This is what I'd like to achieve because I want to allow for rendering the <SearchFilter/> component at one place in the tree, and allow the <SearchResults/> component to be rendered elsewhere, so that there's maximum flexibility in how the tree is composed and, therefore, how the view is rendered.
I've looked into the Context API, but it seems like that would require a handful more components to be a part of my library, which further complicates what I'm trying to achieve. If that's the only way around it, then that's fine, but I wanted to ask and see if anyone can think of another solution.
Thanks!
The bigger issue is that you will need to manage a state that is shared between components on a higher level, i.e., any component that will wrap these other two components, ultimately. With plain React, this state would be managed by the parent (or ancestor) component, passing down the relevant values as props. This opposed to the, usually bad, idea to have sibling components influence each other's state, since you well get into the "who's boss here"-problem.
The thing the Context API handles is not having to pass down props for things that typically don't change (or: typically shouldn't cause renders to trigger often).
A global state store, such as Redux, can help you modelling this, but in essence it's not much more than 'a' component managing state, and other components rendering according to that state. Events within the lower components trigger changes in the data, which will cause the state to change, which will cause the props of the children to change, which then will cause re-renders.
I'd advise you to try using this simple pattern:
class Search ... {
state = {data: [], text: ""}
render() {
return (
<div>
<SearchFilter
data={this.state.data}
onSearch={() => this.fetchNewData()}
onChange={(e) => this.setState({text: e.targetElement.value})}
text={this.state.text}
/>
<SearchResults data={this.state.data} />
</div>
);
}
fetchNewData() {
fetch('/url?text=' + this.state.text)
.then((newData) => { this.setState({data: newData}); })
}
}
Something along these lines. If you have trouble modelling stuff like this, you can use Redux to force you to do it in a similar way, and avoid managing local state intermixing with global state (which is typically something that is hard to manage).
If you do this right, components that have no state (i.e., aren't responsible for managing state and thus have no event handlers) can all become pure components, i.e. stateless components, i.e. functions that return JSX based on props:
const SearchResults = ({data}) => <div>{data.map( () => <etc /> )}</div>
You could create a data store class that holds your filter, pass it in as a property to both components, and have your SearchFilter component change a value in that.

Categories