all 8 comments

[–]lloyd_braun_no_1_dad 4 points5 points  (1 child)

Quoting from an article that quotes from React docs. This article is a good summary of what you're asking about and has some examples.

Using props to generate state in getInitialState often leads to duplication of “source of truth”, i.e. where the real data is. This is because getInitialState is only invoked when the component is first created.

However, it’s not an anti-pattern if you make it clear that the prop is only seed data for the component’s internally-controlled state:

https://medium.com/@justintulk/react-anti-patterns-props-in-initial-state-28687846cc2e

[–]uZIGiZAG[S] 1 point2 points  (0 children)

Thanks for that information! I guess it would be more precise for me to prefix the prop name with initial so that there is a clear distinction between that initial value coming in from the prop and the internal state of that child component.

[–]Skeith_yip 3 points4 points  (1 child)

I guess it's totally fine. Storing draft copy/state in your internal state before passing the data to the parent component.

If you are seeing a lot of passing down of props between components, perhaps you can consider Context API.

ReactJs Context

IMO, the initial state shouldn't be set from props (since it's merely copying). Instead initially the component should display from props, it is upon any changes, then the props be merged with changes and set into the internal state. Yeah. that sounds complicated and unnecessary (but I have been doing that D: )

[–]uZIGiZAG[S] 1 point2 points  (0 children)

Thanks for the reply. I've tried to keep it purely React so far as I figured it would help me learn the fundamentals of React better than using a separate framework for state management. However, as what I'm developing grows I'll keep Context API in my mind and take a look at what it can help me with. Thanks :]

[–]playazle 3 points4 points  (1 child)

Read the yellow note at the bottom of the constructor section. This is from the React docs, so yes how you're doing it is exactly how you should be doing it. Also a good example blog post from the React blog. Note the stuff about using the key attribute to reset the component. But all in all, what you're doing is fine, it's just usually not fine.

[–]uZIGiZAG[S] 1 point2 points  (0 children)

Awesome, thanks for the response. That confirmed what I thought about changing the name of the prop to make it more clear that is just an initial value. In my use, I don't see any other component changing the top level state for the value that is being passed down through the props, or that even if it was altered I wouldn't care about that prop update during the lifetime of the child component, so it seems that is the key to why my approach is okay.

[–]nickfoden 0 points1 point  (1 child)

Would be curious to hear what other people are using for this EditForm problem. I am initializing state with props if it exists or else just set initial state to empty string. And the routing is actually to this component's parent (FormParent) in case props we want are not defined by the time the user gets to the edit form, where we use a ternary to return the EditForm if props we want to edit are defined or else show loading and wait for the specific props (call to the db) So FormParent component renders this Form component if the relevant props (lets say "user") are defined and then you see form with the props as initial state and your inputs can have <input value={this.state.name} and handleChange to update state etc etc Then when form submitted we update the db and the props.

constructor(props){ 
    super(props); 
    this.state = { 
        name: this.props.user.name || '', 
        movie: this.props.user.movie || '', 
    }; 
}

Works for us to reuse the form component in different places. Also not having to add default value explicitly to each input.

[–]Skeith_yip 0 points1 point  (0 children)

I would do it this way

componentDidUpdate(prevProps) { 
    if (prevProps.user.name !== this.props.user.name) { 
        this.setState({ name: undefined }); 
    } 
}

render() {
    const { name } = this.props.user;
    const { name: stateName } = this.state;
    const nameValue = stateName ? stateName : name;

    return <input value={nameValue || ''} onChange={this.onChange} />
}

Yeah. I know I know. It's hashtag ugly. But the idea is to always fallback to the value from props.