GithubHelp home page GithubHelp logo

Props change does not re render about react-guide HOT 18 OPEN

ubervu avatar ubervu commented on August 23, 2024 12
Props change does not re render

from react-guide.

Comments (18)

NiGhTTraX avatar NiGhTTraX commented on August 23, 2024 62

@jeff-lau a new set of props coming in for a component will always cause it to try to re-render (I say try because you can stop rendering with shouldComponentUpdate). Think of components as functions and of props as the arguments to the functions. Receiving props is like calling the function. If you call add(1, 2) you expect 3. If you call it again with add(3, 3) you expect 6.

componentWillReceiveProps can be used to react to incoming new props and possibly trigger state changes, but a component will always try to re-render regardless if you call setState or not in that life cycle method.

from react-guide.

NiGhTTraX avatar NiGhTTraX commented on August 23, 2024 44

In your example you're mutating the this.props object which is not allowed because React won't notice the change. The props do indeed change but the component is unaware of this. That's why you need to forcefully trigger an update, either by calling setState or forceUpdate. Then, React will see the new value for this.props and use it.

You should never manually update this.props. If you want a component to send new props to a child component then those new props should be computed at render time e.g. as a derivation of the parent's state.

Container {
  state= { foo: 123 }
  handleEvent() {
    this.setState({ foo: 456 })
  },

  render() {
    return <Child foo={this.state.foo} />
}

Child {
  render() {
    return <div>{this.props.foo}</div>
  }
}

from react-guide.

jeff-lau avatar jeff-lau commented on August 23, 2024 28

@NiGhTTraX thank you for replying!

I find that, if a parent container changes the props passed in to a child component. The child component does not re-render automatically. The way I can get it to re-render is to update the state of the child component with the new props in the componentWillReceiveProps method.

https://stackoverflow.com/questions/38892672/react-why-child-component-doesnt-update-when-prop-changes

That seems to be the way how react behaves?
thanks,

from react-guide.

phtmgt avatar phtmgt commented on August 23, 2024 15

Guys, I am having a problem with re-rendering of a component (and came here Googling) on its props change:

Component rendered in parent:

<JobList project={this.state.element} jobs={this.state.activeJobs} user={this.props.user} onChange={handleChange} title="Active Jobs"/>

Change state in parent:

const handleChange = (...) => {
  ...
  this.setState({
    exclusionList: exclusionList,
    activeJobs: activeJobs,
    completedJobs: completedJobs,
  });
  ...
}

onClick method in child:

const handleClick = (job, action) => {
	      	this.props.onChange(job, action);
	    }

<button className="btn btn-icon btn-danger btn-simple btn-round btn-sm" onClick={() => handleClick({value:element.id, title: element.title, complete: element.complete}, 'removeJob')}><i className="fal fa-times"></i></button>

Set state fires for sure, as another component, that takes its props from state re-renders immediately:

<Progress value={(this.state.completedJobs.length/(this.state.activeJobs.length + this.state.completedJobs.length))*100} />

I hope I am not hijacking the thread.

EDIT: RESOLVED!

I found my (probably newbie mistake). It was very simple: I was assuming that the constructor of the child would be called whenever props is changed and was copying props to state, then using state in the render functions. Apparently, constructor of child did not run when props were changed. Once I started using props instead of state in child function everything started working.

from react-guide.

solzhang777 avatar solzhang777 commented on August 23, 2024 13

I think should be use Vuex, put the model object for react with parent and children components. in vue template(ui), you can bind the object (from $store.state.object). when the object changed by this.$store.commit('updateObject', newObject); your children component will be render. The means: parent, children component, even sibling components share same model object with Vuex

from react-guide.

Anastasiia-F avatar Anastasiia-F commented on August 23, 2024 3

Hello. Above in thred we have a lot clarification about why props does not trigger re-render. But the React docs tells that they should do. Why?

An update can be caused by changes to props or state.

https://reactjs.org/docs/react-component.html#updating

from react-guide.

Guneetgstar avatar Guneetgstar commented on August 23, 2024 2

@jeff-lau a new set of props coming in for a component will always cause it to try to re-render (I say try because you can stop rendering with shouldComponentUpdate). Think of components as functions and of props as the arguments to the functions. Receiving props is like calling the function. If you call add(1, 2) you expect 3. If you call it again with add(3, 3) you expect 6.

I didn't test it with react class components but whats has happened to me while using react functional components and redux changing props is this:

  const addresses = props.addresses
  const [currentAddress, setCurrentAddress] = useState(addresses && addresses[0])

I am using both the props and the state of addresses. It seems like my props didn't apply to the currentAddress when the render function is called but this could be a side effect of using useState as I had to manually change the state every time the address changes as the render function must have been called otherwise I won't be able to see my props getting changed:

  useEffect(() => {
    setCurrentAddress(addresses && addresses[0])
  }, [addresses])

from react-guide.

swashata avatar swashata commented on August 23, 2024 1

@Guneetgstar The combination of useState and useEffect as you have done, is the right way to keep state and props in "Sync". The reason why a prop change wouldn't update your UI is

  1. When react first renders the component it (per your code) takes the addresses prop and creates a state currentAddress.
  2. When addresses is changed, then react of course re-renders your component.
  3. BUT, the useState(addresses && addresses[0]) does not take into account the value of the expression addresses && addresses[0]. Because it already has a state value from previous render.
  4. So your UI doesn't show the new value of the expression addresses && addresses[0].

But here is the thing, in your example, you do not need to create a state at all. If what you want is the value of the expression addresses && addresses[0] and show it somewhere in the UI, then why bother storing it in a state? State is supposed to be something that is local to the component, there is really no need to sync it with your props. Rather just derive what ever you want to show from the props and show it directly. Like

export function MyComponent(props) {
  const { addresses } = props;

  return (
    <div>Address is {addresses ? addresses[0] : 'Unknown'}</div>
  );
}

This is a very common mistake to think that we need to sync the state with the prop to show in the UI, I did similar mistakes earlier. I would really recommend you to read this article by @kentcdodds

https://kentcdodds.com/blog/dont-sync-state-derive-it

from react-guide.

swashata avatar swashata commented on August 23, 2024 1

@Guneetgstar I see. Your use case may vary, but it is also possible to do something like

const { addresses, dispatch } = props;
const currentAddress = addresses ? addresses[0] : '';

return (
  <input type="text" value={currentAddress} onChange={e => {
    const newAddresses = addresses ? [...addresses] : [];
    newAddresses[0] = e.target.value;
    // call redux dispatch to update address
    dispatch({
      type: 'UPDATE_ADDRESS',
      payload: newAddresses,
    });
  }} />
);

But of course it would update your redux store on every keystroke. If you are mapping only needed props to your components, then it wouldn't matter much. Although if performance does become an issue then you can copy the prop to state, keep then in sync and then debounce any update to the local state through your redux dispatcher.

from react-guide.

swashata avatar swashata commented on August 23, 2024

Thanks @NiGhTTraX for the clarification. I am just starting with React and I wasn't sure that updating the props from outside will cause re-render (because this is what I need). I came across the stackoverflow example and I thought it is indeed not okay to change props inside a component. Your clarification clears things up.

Bottom line is I am passing props from parent to child without of course mutating them ever. So if the prop does change (from an even external component, like GrandParent) then react will re-render.

The only situation I have found to actually look for prop change is, if I am using one of the props to set an initial state. Then I would not set any state at constructor. I would rather use getDerivedStateFromProps(nextProps, prevState) and do it there. I may compare against prevState to check if it is actually necessary for me to update.

Further down I may use shouldComponentUpdate to fine tune it further.

So please correct me if my understanding is wrong. Thank you.

from react-guide.

NiGhTTraX avatar NiGhTTraX commented on August 23, 2024

@swashata your understanding is correct. I just want to clarify one small thing for future readers:

I would not set any state at constructor. I would rather use getDerivedStateFromProps(nextProps, prevState) and do it there

At least with the current API in 16.3.0, state needs to be defined in the constructor before calling getDerivedStateFromProps. If you don't initialize it then you get the following warning:

Warning: Foo: Did not properly initialize state during construction.
Expected state to be an object, but it was undefined.

getDerivedStateFromProps is meant to replace componentWillReceiveProps so its main purpose will be to adjust existing state based on new incoming props, not to define the initial state.

from react-guide.

swashata avatar swashata commented on August 23, 2024

@NiGhTTraX Thanks for the pointer. I was already setting this.state = {...} with some initial value. I did face the error when I didn't do it.

Bdw, could you please do a quick review of the codebase I am working on? I will be very glad 😅

from react-guide.

ryan2clw avatar ryan2clw commented on August 23, 2024

Great pointer about the forceUpdate when new props are passed to the component in some edge case scenarios. I may try the getDerviedStateFromProps in the future. I agree with your comment @swashata that "The only situation I have found to actually look for prop change is, if I am using one of the props to set an initial state", plus of course setting static items. I ran into a conundrum earlier today where I wanted to get the derived state from the initial props, so I did this https://stackoverflow.com/questions/50103629/passing-data-from-ajax-call-to-view-in-react-native. Seems to work for me, thought I'd pass the knowledge along. Thanks for clearing some things up for me @NiGhTTraX; reading this conversation helped validate my solution.

from react-guide.

bologer avatar bologer commented on August 23, 2024

@NiGhTTraX I have a small question:

I have shouldComponentUpdate() in child component. For the first render, props seems to be passed, on the state change of the parent shouldComponentUpdate() seems missing the props.

Is is somewhat expected?

from react-guide.

NiGhTTraX avatar NiGhTTraX commented on August 23, 2024

Hey @bologer, can you provide some example code?

from react-guide.

Microserf avatar Microserf commented on August 23, 2024

Thank you, @finkbg. I had made the exact same wrong assumption about the constructor() function running each time the properties changed. Your post helped me understand the issue I was having, and how to fix.

from react-guide.

bizcontact avatar bizcontact commented on August 23, 2024

In your example you're mutating the this.props object which is not allowed because React won't notice the change. The props do indeed change but the component is unaware of this. That's why you need to forcefully trigger an update, either by calling setState or forceUpdate. Then, React will see the new value for this.props and use it.

You should never manually update this.props. If you want a component to send new props to a child component then those new props should be computed at render time e.g. as a derivation of the parent's state.

Container {
  state= { foo: 123 }
  handleEvent() {
    this.setState({ foo: 456 })
  },

  render() {
    return <Child foo={this.state.foo} />
}

Child {
  render() {
    return <div>{this.props.foo}</div>
  }
}

Excellent! Child should not keep this as state if it is property replication. If it is transforming then event passing and transform again.

from react-guide.

Guneetgstar avatar Guneetgstar commented on August 23, 2024

@swashata I acknowledge your suggestion but there was a need to store currentAddress in the local state as currentAddress is something that is a candidate in my UI that might be saved in redux and hence further propagated to the props. But until the user don't save the currentAddress it must be available in the UI only as user might edit it.

+1 for 3rd point.

from react-guide.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.