Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass nested properties with spread attributes correctly? (JSX)

#1

Hello. I have the code:

class Component extends React.Component {
    render() {
        this.props.nested.prop = this.props.parse.nested.prop;
        return <div>Component</div>;
    }
    componentDidMount() {
        console.log(this.props.nested.prop);
    }
}
Component.defaultProps = {
    nested: {
        prop: "default",
    },
};

const obj1 = {
    nested: {
        prop: "obj1",
    },
};
const obj2 = {
    nested: {
        prop: "obj2",
    },
};

class Application extends React.Component {
    render() {
        return (
            <div>
                <Component parse={obj1} />
                <Component parse={obj2} />
            </div>
        );
    }
}

React.render(<Application />, document.getElementById("app"));
// console output:
// "obj2"
// "obj2"

Why do I get 1 variable reference for 2 separate components instead of 2 instances of nested.prop for every component? Why this.props saves only last setted value for all instances of the component after mounting? Is it a normal behavior? I think the correct behavior is having different property values for different instances.

P.S. I have tested this code here.

#2

jimfb has been answered: "You are mutating the default prop that was passed in. The line this.props.nested.prop = this.props.parse.nested.prop; is illegal."

My next question: How to pass nested properties without a manual mutation of props?

For example:

Component.defaultProps = {
    nested: {
        prop1: "default",
        prop2: "default",
    },
};

const props = {
    nested: {
        prop1: "value",
    },
};

let component = <Component {...props} />;

Guide to the code above JSX spread attribute feature just override props.nested and I lose default nested properties. But it is not that I need. How about to implements a recursive traversing of nested objects in stage of JSX spread attributes parsing?
Or Is there some useful pattern for this case?

like image 987
Kirill A. Khalitov Avatar asked Mar 28 '16 15:03

Kirill A. Khalitov


Video Answer


1 Answers

This is actually a good question!

Short answer: you can't do deep merge with spread operator - it only does shallow merging. But you surely can write function that will do objects traversing and implements deep merging.

This actually leaves you 3 options:

1) Just don't do deep merge. If you have 2 level nested object you can do such simple thing:

const newNested = {...oldProps.nested, ...newProps.nested };
const finalProps = { ...oldProps, nested: newNested };

Shallow merging force you to say explicitly that you will have new value of nested property. Which is a good thing as it makes your code obvious. You can also try out runnable examples here.

2) You may use a library for immutable structures. F.e. immutable.js. With it your code will look pretty similar.

const oldProps = Immutable.fromJS({
  nested:
  {
    prop1: "OldValue1",
    prop2: "OldValue2",
  }
});

const newProps = Immutable.fromJS({
  nested:
  {
    prop1: "NewValue1",
  }
});

const finalProps = oldProps.updateIn(['nested'], (oldNested)=>
  oldNested.merge(newProps.get('nested'))
)

3) You may use deep merge: find some implementation in npm or write it yourself and you will have a code like this(again immutable.js as an example):

const finalProps = oldProps.mergeDeep(newProps);

You may want this in some cases, but such code makes update operation implicit and it rises a lot of problems which are listed greatly here

like image 72
Sergey Lapin Avatar answered Sep 22 '22 22:09

Sergey Lapin