The render method of this component does use any of the props supplied to the component.
Will the component re-render when the props change regardless?
class MyComponent extends React.Component {
constructor(props) {
super(props);
const { propValue } = props;
// do something with propValue...
}
render () {
return (
<div>foo</div>
);
}
}
Will render be called - yes. Unless you implement shouldComponentUpdate to return false.
Will the DOM be rerendered - no.
Also you might want to take a look at https://babeljs.io/docs/plugins/transform-react-constant-elements/ that hoists static elements up.
In
const Hr = () => {
return <hr className="hr" />;
};
Out
const _ref = <hr className="hr" />;
const Hr = () => {
return _ref;
};
Yes, the component will re-render unless you implement shouldComponentUpdate. You can inherit from PureComponent which uses shallow comparison of prop and state with previous values to determine if component should update or not.
As far as i know react will call the render method in the following scenarios
when your component get mounted initially
when state got changed using this.setState()
when your component receives new props
when this.forceUpdate() get called.
since you didn't implement shouldcomponentUpdate() the render method is going to get called
Related
I have two components. A main and a child component.
Let's assume a function is triggered in the main component which cause its state to be mutated.
The state of the main component is passed down to the child component as a prop. The newly updated data in the props of the child component should now be used to to set the state of the child component.
I can't do this on ``componentDidUpdate since it would cause an infinite loop.
On the other hand I wouldn't want to lift the child's state to the main component since most code of it would be useless in the main component.
I hope you can help
You can use getDerivedStateFromProps as mentioned in the React docs:
export default class Child extends Component {
static getDerivedStateFromProps(newProps, currentState) {
return {
value : newProps.value
}
}
render() {
return (
<div>
{/* Your layout */}
</div>
);
}
}
componentDidUpdate takes prevProps as argument componentDidUpdate(prevProps, prevState, snapshot). So to not getting the code in infinite loop, you can compare this.props with prevProps and update the state accordingly.
componentDidUpdate(prevProps) {
if(this.props.data !== prevProps.data) {
// update the new state here this will not cause infinite loop
}
}
For a functional component using hooks.
function Child(props) {
const [whatever, setWhatever] = React.useState(props.whatever);
React.useEffect(() => {
setWhatever(props.whatever);
}. [whatever]);
}
export default Child;
Hope it helps.
I am writing a higher-order component that takes children and then re-renders them based on the state of a context provider.
Consider the following simplified example:
index.js
const ChildElem = () => {
return(
<div/>
)
}
class Example extends React.Component{
render(){
return(
<FocusProvider>
<ChildElem/>
<ChildElem/>
<ChildElem/>
</FocusProvider>
)
}
}
FocusProvider.js
class FocusProvider extends React.Component{
renderChildren = (providerState) => {
//Does nothing with state and simply returns children yet they still re render
return this.props.children
}
render(){
return(
<Provider>
<Subscribe to={[ContextProvider]}>
{provider => this.renderChildren(provider.state)}
</Subscribe>
</Provider>
)
}
}
As you can see from the example the children of FocusProvider are being returned from a function that subscribes to a context.
The problem I am running into is that the children are being re-rendered even though nothing is being changed on them. The only thing that is being changed is the state of the context provider they are subscribed to.
Any advice would be greatly appreciated
You can control whether the component should update or not there is a function of react component class
shouldComponentUpdate ( nextProps, nextState, nextContext ) {
/* compare nextState with your current states properties if you want to update on any basis return true if you want to render the component again */
return true; // will re-render component ,
return false; // do not re-render component even if you change component states properites
}
nextProps contains that prop the new props , and nextState contain new State properties
I have some heavy forms that I'm dealing with. Thus, I'm trying to squeeze performance wherever I can find it. Recently I added the Why-did-you-render addon to get more insight on what might be slowing down my pages. I noticed that, for example, when I click on a checkbox component about all of my other components re-render. The justification is always the same. WDYR says
Re-rendered because of props changes: different functions with the
same name {prev onChangeHandler: ƒ} "!==" {next onChangeHandler: ƒ}
As much as possible, I try to respect best the best practices indications that I find. The callback functions that my component passes follow this pattern
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
export function TopLevelComponent({props}){
const defaultData = {name: '', useMale: false, useFemale: false}
const [data, setData] = useState(defData);
const { t } = useTranslation();
const updateState = (_attr, _val) => {
const update = {};
update[_attr] = _val;
setData({ ...data, ...update });
}
const updateName = (_v) => updateState('name', _v);//Text input
const updateUseMale = (_v) => updateState('useMale', _v);//checkbox
const updateUseFemale = (_v) => updateState('useFemale', _v);//checkbox
...
return <div>
...
<SomeInputComponent value={data.name} text={t('fullName')} onChangeHandler={updateName} />
<SomeCheckboxComponent value={data.useMale} onChangeHandler={updateUseMale} text={t('useMale')}/>
<SomeCheckboxComponent value={data.useFemale} onChangeHandler={updateUseFemale} text={t('useFemale')}/>
...
</div>
}
In an example like this one, altering any of the inputs (eg: Writing text in the text input or clicking one of the checkboxes) would cause the other 2 components to re-render with the justification presented above.
I guess that I could stop using functional components and utilize the shouldComponentUpdate() function, but functional components do present some advantages that I'd rather keep. How should I write my functions in such a way that interacting with one input does not force an update on another input?
The problem stems from the way you define your change handlers:
const updateName = (_v) => updateState('name', _v)
This line is called on each render and thus, every time your component is rendered, the prop has a new (albeit functionality-wise identical) value. The same holds for every other handler as well.
As an easy solution you can either upgrade your functional component to a fully fledged component and cache the handlers outside of the render function, or you can implement shouldComponentUpdate() in your child components.
You need to use memo for your child components to reduce renders
const SomeInputComponent = props => {
};
export default memo(SomeInputComponent);
// if it still causes rerender witout any prop change then you can use callback to allow or block render
e.f.
function arePropsEqual(prevProps, nextProps) {
return prevProps.name === nextProps.name; // use your logic to determine if props are same or not
}
export default memo(SomeInputComponent, arePropsEqual);
/* One reason for re-render is that `onChange` callback passed to child components is new on each parent render which causes child components to re-render even if you use `momo` because function is updated on each render so in order to fix this, you can use React hook `useCallback` to get the same function reference on each render.
So in you parent component, you need to do something like
*/
import { useCallback } from 'react';
const updateName = useCallback((_v) => updateState('name', _v), [])
You have to memoize parent function before pass to children, using useCallback for functional component or converting to class property if you use class.
export default class Parent extends React.PureComponent {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
onClick() {
console.log("click");
}
render() {
return (
<ChildComponent
onClick={ this.onClick }
/>
);
}
}
with useCallback:
Parent = () => {
const onClick = useCallback(
() => console.log('click'),
[]
);
return (
<ChildComponent
onClick={onClick}
/>
);
}
I'd like to know, how to handle the PropTypes Error when passing a component as a child:
Failed prop type: The prop `value` is marked as required in `ChildComponent`, but its value is `undefined`.
The render works as expected and it's passing the value prop correctly.
I suppose this happens because I am putting the component in the App component's render function without any props.
I am only passing those props to the ChildComponent when the ParentComponent maps over its children (which is the ChildComponent).
See the code: https://codesandbox.io/embed/r70r5z3j9q
Is there a way to prevent this from happening?
How should I be structuring my components?
Am I not supposed to passed components as children?
EDITED: Changed prop "name" to "value". To give it a more generic feel.
I tried to simplify the problem in the code.
I know I could pass the prop directly in App.
The use case would be when the parent is doing calculations and those calculations are supposed to be passed to the child. Without explicitly knowing what these children are.
That's why I'm using it as child in the first place.
You're using cloneElement and you're passing prop to it, not to original element. To fix it, pass props directly:
const App = () => (
<div>
<ParentComponent>
<ChildComponent name="bob" />
</ParentComponent>
</div>
);
You could easily pass component as a prop (not children) to you ParentComponent and render it only after it takes some heavy calculations:
const App = () => (
<div>
<ParentComponent component={ChildrenComponent} />
</div>
);
const ParentComponent extends React.Component {
state = { heavyComputationFinished: false } // initial state
componentDidMount() {
runYourHeavyComputations
.then(() => { this.setState({ heavyComputationsFinished: true }) })
}
render() {
const { component } = this.props
const { heavyComputationsFinished, name } = this.state
// return nothing if heavy computations hasn't been finished
if (!heavyComputationsFinished) { return null }
// we're getting this component (not its rendering call) as a prop
return React.render(component, { name })
}
}
I'm using React + Electron + Redux in my app development. I was able to update the parent state from a child component in another case, but now I'm not able to do it, the state is only being updated to the child components.
I know that the reducer action is being called with the right value, but the parent component is being rerendered with the wrong one (the previous one), only the sub tree of the child component is being rendered with right value.
My method:
I'm creating a function (action handler) in the parent component container:
class CreateExerciseCanvas extends React.Component {
focusOnSection (section) { /* this is the function that i'm refering to */
store.dispatch(actions.focusOnSection(section))
}
render() {
return (
<CreateExerciseCanvas
focusOnSection={ this.focusOnSection }
/>
)
}
}
const mapStateToProps = function (store) {
return {
focusOnSection: store.exercise.focusOnSection
}
}
export default connect(mapStateToProps)(CreateExerciseCanvasContainer)
And this function is being passed as a prop to the child container:
<Index focusOnSection={ this.props.focusOnSection }/>
Lastly, the method is being used as an onClick handler in the child view.
Isn't this the right way of updating a parent with redux + react?
You have to bind your this context to the focusOnSection function in your constructor, or else it doesn't know what this is.
Try adding a constructor like so to your CreateExerciseCanvas:
constructor(props) {
super(props);
this.focusOnSection = this.focusOnSection.bind(this);
}
This is probably this most annoying part about using ES6 classes.
If you check the value of this.props inside focusOnSection (section), you will see that it is undefined. This is because focusOnSection () {} is the short syntax of focusOnSection: function () {}, which is binding this to the function, so there is no more this.props.
One solution would be hard-binding this to the class in the constructor:
constructor(props) {
super(props);
this.focusOnSection = this.focusOnSection.bind(this);
}
The other would be using an arrow function like focusOnSelection = () => {} which doesn't bind this. This latter solution only works if you are using babel (check the es2015 preset).