Skip to content

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?

Answer

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