React: Do children always rerender when the parent component rerenders? - javascript

It is to my knowledge that if a parent component rerenders, then all its children will rerender UNLESS they implement shouldComponentUpdate(). I made an example where this doesn't seem to be the true.
I have 3 components: <DynamicParent/>, <StaticParent/> and <Child/>. The <Parent/> components are responsible for rendering the <Child/> but do so in different ways.
<StaticParent/>'s render function statically declares the <Child/> before runtime, like so:
<StaticParent>
<Child />
</StaticParent>
While the <DynamicParent/> handles receiving and rendering the <Child/> dynamically at runtime, like so:
<DynamicParent>
{ this.props.children }
</DynamicParent>
Both <DynamicParent/> and <StaticParent/> have onClick listeners to change their state and rerender when clicked. I noticed that when clicking <StaticParent/> both it and the <Child/> are rerendered. But when I click <DynamicParent/>, then only the parent and NOT <Child/> are rerendered.
<Child/> is a functional component without shouldComponentUpdate() so I don't understand why it doesn't rerender. Can someone explain why this is to be the case? I can't find anything in the docs related to this use case.

I'll post your actual code for context:
class Application extends React.Component {
render() {
return (
<div>
{/*
Clicking this component only logs
the parents render function
*/}
<DynamicParent>
<Child />
</DynamicParent>
{/*
Clicking this component logs both the
parents and child render functions
*/}
<StaticParent />
</div>
);
}
}
class DynamicParent extends React.Component {
state = { x: false };
render() {
console.log("DynamicParent");
return (
<div onClick={() => this.setState({ x: !this.state.x })}>
{this.props.children}
</div>
);
}
}
class StaticParent extends React.Component {
state = { x: false };
render() {
console.log("StaticParent");
return (
<div onClick={() => this.setState({ x: !this.state.x })}>
<Child />
</div>
);
}
}
function Child(props) {
console.log("child");
return <div>Child Text</div>;
}
When you write this code in your Application render:
<StaticParent />
What's rendered is this:
<div onClick={() => this.setState({ x: !this.state.x })}>
<Child />
</div>
And in reality, what happens (roughly) is this:
function StaticParent(props) {
return React.createElement(
"div",
{ onClick: () => this.setState({ x: !this.state.x }) },
React.createElement(Child, null)
);
}
React.createElement(StaticParent, null);
When you render your DynamicParent like this:
<DynamicParent>
<Child />
</DynamicParent>
This is what actually happens (again, roughly speaking)
function DynamicParent(props) {
return React.createElement(
"div",
{
onClick: () => this.setState({ x: !this.state.x }),
children: props.children
}
);
}
React.createElement(
DynamicParent,
{ children: React.createElement(Child, null) },
);
And this is the Child in both cases:
function Child(props) {
return React.createElement("div", props, "Child Text");
}
What does this mean? Well, in your StaticParent component you're calling React.createElement(Child, null) every time the render method of StaticParent is called. In the DynamicParent case, the Child gets created once and passed as a prop. And since React.createElement is a pure function, then it's probably memoized somewhere for performance.
What would make Child's render run again in the DynamicParent case is a change in Child's props. If the parent's state was used as a prop to the Child, for example, that would trigger a re-render in both cases.
I really hope Dan Abramov doesn't show up on the comments to trash this answer, it was a pain to write (but entertaining)

It's mainly cause of you have 2 different "children".
this.props.children
<Child/>
They're not the same thing, first one is a prop passed down from Application -> DynamicParent, while the second one is a Component rendered in StaticParent, they have separate rendering/life cycles.
Your included example
class Application extends React.Component {
render() {
return (
<div>
{/*
Clicking this component only logs
the parents render function
*/}
<DynamicParent>
<Child />
</DynamicParent>
{/*
Clicking this component logs both the
parents and child render functions
*/}
<StaticParent />
</div>
);
}
}
Is literally the same as:
class Application extends React.Component {
render() {
// If you want <Child/> to re-render here
// you need to `setState` for this Application component.
const childEl = <Child />;
return (
<div>
{/*
Clicking this component only logs
the parents render function
*/}
<DynamicParent>
{childEl}
</DynamicParent>
{/*
Clicking this component logs both the
parents and child render functions
*/}
<StaticParent />
</div>
);
}
}

As a comment to SrThompsons answer: "What would make Child's render run again in the DynamicParent case is a change in Child's props. If the parent's state was used as a prop to the Child, for example, that would trigger a re-render in both cases."
So props or not props passed to child component however it may look will cause a rerender if parent rerenders (so use React.memo for a child without props :) )
"Whether you’re implementing your component as a class component that extends React.Component, or as a functional component, the render function is called again whenever the parent container renders again." Please read here for more info, because great article. https://medium.com/free-code-camp/yeah-hooks-are-good-but-have-you-tried-faster-react-components-e698a8db468c"

It will only re-render components that have had a change. If nothing on the child component has changed, it will not be re-rendered.

Related

React render JSX from method

In order to keep the render method of my component shorter and also avoid creating additional components I was hoping to render the HTML from a class method depending on the state like so:
class ExampleComponent extends React.Component {
constructor (props) {
super(props);
this.state = {
step: 'step1'
}
}
handleChangeStep(step) {
this.setState({ step: step })
}
step1 () {
return (
<>
<h2>Step 1</h2>
<div onClick={this.handleChangeStep('step2')}>Next Step</div>
</>
)
}
step2 () {
return (
<>
<h2>Step 2</h2>
<div onClick={this.handleChangeStep('step1')}>Previous Step</div>
</>
)
}
render () {
return this.state.step === 'step2' ? this.step2() : this.step1();
}
}
However this gives the error: Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state..
Seems you can't do this and instead need to do something like (from: https://reactjs.org/docs/conditional-rendering.html)
return this.state.step === 'step2' ? <Step1 /> : <Step2 />
However I don't understand why the former example isn't allowed as those two methods just return JSX so in theory should allow you to return one or the other depending on the state.
The problem has nothing to do with what you're describing. You're trying to update state during a render:
onClick={this.handleChangeStep('step2')}
When this.handleChangeStep('step2') is invoked, state is updated. Updating state triggers a re-render. Which in this case would then update state again, and again, indefinitely.
I suspect you meant to pass a function reference to onClick, not invoke a function:
onClick={() => this.handleChangeStep('step2')}

Changing the state in reactJs parent is not changing the props in react child components

I have a Parent React Component and which have 3 Child components i am changing the state in parent component but changing the state in parent is not changing the props in child components.I am passing the state from parent to child components but the props are not changing inside the child components.
My parent component
class Parent extends Component {
state = {
menuCategoryId:'',
}
handelOnClickRefundMenu = () => {
this.setState({menuCategoryId:''});
}
render() {
return (
<FoodMenu
menuCategories={menuCategories}
{...this.state}
/>
)
}
}
export default Parent;
Child 1 Component
class FoodMenu extends Component {
render() {
return (
<MenuCategories
MenuCategories={menuCategories.MenuCategories}
selectedMenuCategoryId={this.props.menuCategoryId}
/>
);
}
}
export default Child1;
Child 2 component
class MenuCategories extends React.Component{
render(){
const MenuCategories = this.props.MenuCategories;
const selectedMenuCategoryId = this.props.selectedMenuCategoryId;
const renderCategories = (MenuCategories) => (
MenuCategories ?
MenuCategories.map(card=>(
<MenuCategory
key={card._id}
{...card}
handleOnClickMenuCategory={this.props.handleOnClickMenuCategory}
selectedMenuCategoryId={this.props.selectedMenuCategoryId}
// propData={...this.props}
/>
))
:null
)
return (
<Fragment>
<div id="" className="food-menus-menu w-100">
<div className="row">
<OwlCarousel
className="owl-theme"
loop={true}
items={9}
autoplay={false}
autoplayTimeout={3000}
autoplayHoverPause={false}
nav={true}
navElement={'div'}
navText={[`<img src=${seventheenPng}>`,`<img src=${eitheenPng}>`]}
dots={false}
responsive={{
0:{
items:4
},
600:{
items:3
},
1000:{
items:7
}
}}
>
{MenuCategories ?
MenuCategories.length === 0 ?
<div className="no_result">
Sorry, no results
</div>
:null
:null}
{ renderCategories(MenuCategories)}
</OwlCarousel>
</div>
</div>
</Fragment>
)
}
};
export default MenuCategories;
Child 3 Component
class MenuCategory extends Component {
render() {
const props = this.props;
console.log('The values of the props are not changing here')
console.log(props.selectedMenuCategoryId)
return (
<div className={`colCategory item`} onClick={()=>props.handleOnClickMenuCategory(props)}>
<button
className={`btn btn-primary w-100 py-2 d-inline-flex align-items-center justify-content-center ${props.selectedMenuCategoryId === props._id ? 'activeMenuCategory' : ''}`}>
{props.name}
</button>
</div>
);
}
}
export default MenuCategory;
The value of props "props.selectedMenuCategoryId" in my last component which is inside the Map function MenuCategory is not changing when i change the state in my Parent Class function handelOnClickRefundMenu
The Map function is inside Child Component 2 MenuCategories .
Kindly help me on this please.
Thanks in advance.
All the answers about forcing re-renders using lifecycle methods are wrong. If you pass the props down correctly and they change, your child components should re-render automatically.
To demonstrate this, here's a quick'n'dirty sandbox that has a parent and two children that passes a prop down as you require.
I don't know exactly what's wrong with your code (a self-contained example that we can run and debug would help here), but I suggest paring it back to a simpler case that you can get working and then build up from there.
eta: Are you sure the problem isn't to do with your click handler? You're not passing it in to FoodMenu or MenuCategories.
Why are you naming your prop and variables the same as your component? It’s really hard to read and is probably causing confusion as to whether you’re referencing the component or the prop/variable. Use camel case
Naming your prop MenuCategories inside the component MenuCategories is not only bad practice, but could solve the issue if named menuCategories instead.

Manipulating DOM in componentDidMount() without setTimeout

I want to manipulate the DOM in ReactJS in the componentDidMount() method. My problem is that at this time the DOM isn't fully rendered somehow and I need a setTimeout function, which I would rather omit.
When I console.log the scrollHeight of the rendered element in componentDidMount() it gives me a different number as when I wait for let's say 100 milliseconds.
What I want to achieve is to scroll down to the end of an element which is described here How to scroll to bottom in react?
The component is a modal-window which renders {this.props.children} of another component. The modal-window is rendered into the DOM with visibility: hidden and opacity: 0 and it has the height of the window, when it first appears on the page. By clicking on a button it shows up and still has the height of the window until I wait some milliseconds.
I guess, I do something wrong here when setTimeout is needed, but I didn't found out what.
I also tried to change the DOM in the componentDidUpdate() method with the same results.
I wrote this code in the modal-window component:
componentDidMount() {
console.log(document.querySelector('.myModal').scrollHeight);
setTimeout(function() {
console.log(document.querySelector('.myModal').scrollHeight);
}, 100);
}
First console.log gives me for example 497 and the second one something like 952.
Update
I have a modal-window component which renders a child like this for example for my inbox-thread:
<Modal>
<InboxThread />
</Modal>
The problem was, that I needed to wait until the modal-window component rendered its children like this in the Modal.js:
render() {
return (
<React.Fragment>
{this.props.children}
</React.Fragment>
);
}
So my solution in the end was to hand over a method in the props from the parent component where I call the modal to check if componentDidUpdate() in Modal.js.
My code looks now like this in the parent component:
...
export default class InboxThreadList extends React.Component {
constructor(props) {
super(props);
this.scrollToModalBottom = this.scrollToModalBottom.bind(this);
}
render() {
return (
<React.Fragment>
...
<Modal onRender={this.scrollToModalBottom}>
<InboxThread/>
</Modal>
</React.Fragment>
)
}
scrollToModalBottom() {
const myModalObject = document.querySelector('.myModal');
myModalObject.scrollTop = myModalObject.scrollHeight;
}
}
And in the Modal.js:
...
export default class Modal extends React.Component {
...
componentDidUpdate() {
if ('onRender' in this.props) {
this.props.onRender();
}
}
render() {
return (
<div className={'myModal'}>
{this.props.children}
</div>
);
}
I know! I still should work with refs instead of document.querySelector and I will do as described here React - Passing ref from dumb component(child) to smart component(parent).
If you use a ref - as long as the element is always rendered in render() - it is guaranteed to resolve before componentDidMount runs:
componentDidMount() {
// can use any refs here
}
componentDidUpdate() {
// can use any refs here
}
render() {
// as long as those refs were rendered!
return <div ref={/* ... */} />;
}
componentDidMount called BEFORE ref callback
So in your case, you might code it a bit like this:
componentDidMount() {
console.log(this.mymodal.scrollHeight)
}
render() {
return <div className="mymodal" ref={ref => this.mymodal = ref} />
}

(ReactJS) Function not passing to child component

I am trying to pass props and functions from a parent to a child component in React. However, when I try to call the function in the child component, I receive the error: "Uncaught TypeError: Cannot read property 'bind' of undefined".
This would suggest that the function created in the parent element is not being accessed by the child component. Am I missing a step in my code that would allow me to reference a parent component function from a child component?
I am defining states as props in my parent component, for use in conditional rendering in my child component. I am also defining functions in the parent component, which I am trying to call in my child component.
Provided below is my code:
Note: The flow is supposed to work as follows: Click Sign Up button > Call SignUpClick function > render "SignUp" component (based on the conditional rendering logic outlined in the child component)
The same flow concept would apply if someone clicked the Sign In button.
Parent Component
export default class Parent extends Component{
constructor(props) {
super(props);
this.state = {
SignUpClicked: false,
SignInClicked: false,
};
this.SignUpClick = this.SignUpClick.bind(this);
this.SignInClick = this.SignInClick.bind(this);
}
SignUpClick() {
this.setState({
SignUpClicked: true,
});
}
SignInClick() {
this.setState({
SignInClicked: true,
});
}
render () {
return (
<div>
<Child />
</div>
)
}
}
Child Component
export default class Child extends Component {
render () {
if (this.props.SignUpClicked) {
return(
<SignUp />
) } else if (this.props.SignInClicked) {
return (
<SignIn />
)
} else {
return (
<div>
<div>
<Button onClick={this.SignUpClick.bind(this)}> Sign Up </Button>
</div>
<div>
<Button onClick={this.SignInClick.bind(this)}>Sign In</Button>
</div>
</div>
)
}
}
}
Change the render method in parent class to pass SignUpClick and SignInClick to child.
return (
<div>
<Child SignInClick={this.SignInClick} SignUpClick={this.SignUpClick}/>
</div>
)
Also, in the child class, access the methods as this.props.SignUpClick and this.props.SignInClick
If you think about it, where would the child component grab the references for those functions ? The parent needs to give the child access to those methods, and how does a parent pass data to a child, with props! In your case you are not passing any prop at all to the child, so here is how your parent render method should look like:
render () {
return (
<div>
<Child
signUpClicked={this.state.SignUpClicked}
signInClicked={this.state.SignInClicked}
onSignUpClick={this.onSignUpClick}
onSignInClick={this.onSignInClick}
/>
</div>
);
}
This is how a parent communicates with a child passing down props that are now accessible with this.props. At this point your Child component render method would look like this:
render () {
const {
signUpClicked,
signInClicked,
onSignUpClick,
onSignInClick,
} = this.props;
if (signUpClicked) {
return(<SignUp />);
} else if (signInClicked) {
return (<SignIn />);
} else {
return (
<div>
<Button onClick={onSignUpClick}> Sign Up </Button>
<Button onClick={onSignInClick}>Sign In</Button>
</div>
)
}
}
I used destructuring to help with readability. Hope this helps!

What's the proper way to pass dependencies between components in React?

Imagine that Component A creates a list of items that Component B needs to display. What's the proper way to pass data from Component A to Component B from their parent?
For example, let's say that Component A's constructor creates a list of items and has a function _getListItems() that returns that list. I'm hoping the parent can then pass that list on to other components via props.
My naive (non-working) implementation has their parent attempting to render the components like this:
render () {
return (
<div>
<h1>Data Test</h1>
<ComponentA ref='compa'/>
<ComponentB items={this.refs.compa._getListItems()}/>
</div>
);
}
....although the code above doesn't work, I hope it illustrates what I'm trying to do.
ps. nOOb to react and javascript, so forgive me if the answer to my question's obvious...
Divide your components into two separate categories.
Presentational Component that has responsibility to display a thing. This component should not have state (except for UI state).
Container Component that knows the data.
https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.skmxo7vt4
So, in your case the data should created by parent of ComponentA and ComponentB and pass the data to both ComponentA and ComponentB via props.
Example:
render(){
let items = this._getListItems();
return (
<div>
<ComponentA items={items} />
<ComponentB items={items} />
</div>
);
}
Edit
Rewrite OP's approach in the comment:
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.state = { stuff: [1,2,3] };
}
render() {
return (
<div>
<ComponentA items={this.state.stuff} />
<ComponentB items={this.state.stuff} />
</div>
);
}
}
Following the accepted answer above, I've just had a (related) EUREKA moment, so I'm going to expand on the answer; when the parent uses its own state to pass props to its children, whenever the parent's state changes, its render() function is called, thus updating the children with the updated state. So you can do stuff like this:
class MyContainer extends React.Component {
constructor(props) {
super(props);
let sltd = this.props.selected
this.state = {
stuff: [1,2,3],
selected: sltd
};
}
_handleAStuff(value) {
this.setState(selected: value)
//do other stuff with selected...
}
_handleBStuff(value) {
this.setState(selected: value)
//do other stuff with selected...
}
render() {
return (
<div>
<ComponentA items={this.state.stuff} selected={this.state.selected} parentFunc={this._handleAStuff.bind(this)} />
<ComponentB items={this.state.stuff} selected={this.state.selected} parentFunc={this._handleBStuff.bind(this)} />
</div>
);
}
}
MyContainer.defaultProps = {
selected: 0
}
class ComponentA extends React.Component {
constructor(props) {
super(props)
}
_handleSelect(value) {
this.props.parentFunc(value.label)
}
render() {
const itm = this.props.items.map(function(values) {
return { value: values, label: values}
})
return (
<div>
<Select
options={itm}
value={this.props.selected}
onChange={this._handleSelect.bind(this)}
/>
</div>
);
}
}
// ComponentB...
The callback pattern above means that ComponentA and ComponentB do not need to maintain state, they simply 'render stuff', which is also pretty cool. I'm beginning to see the power of REACT...

Categories

Resources