Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.Component.defaultProps objects are overridden, not merged?

I'm trying to set a defaultProp with an object literal, but after some time I realized that the React class constructor is not merging the default props with the applied props, so I end up with undefined values for any properties in the defaultProps literal that haven't been included in the applied props. Is there a way to merge default props and applied props, or do I need to break up my object into several props?

class Test extends React.Component {
  constructor(props) {
    super(props);

    //props.test is only {one: false}
    //props.test.two is undefined

  }
  render() {
    return (<div>render</div>)
  }
}

Test.defaultProps = {
  test:  {
    one: true,
    two: true
  }
}


ReactDOM.render(<Test test={{'one': false}}/>, document.getElementById('#test'));

http://codepen.io/adjavaherian/pen/oYNPLz

like image 706
4m1r Avatar asked Nov 04 '16 17:11

4m1r


2 Answers

React only does a shallow merge of the default props and the actual props, i.e. nested default props are overridden instead of merged. This is by design.

See this React issue for more background and reasoning why this is the case and potential workarounds:

aside from the potential perf issues here. one issue with this is how do you handle nested complex structures like arrays? concatenation? Union? what about an array of objects? deep object merging can lead to unexpected behaviour, which is why often implementations allow you to specify a merge strategy such as _. merge. I'm not sure how you would do that in prop type declaration.

like image 172
TimoStaudinger Avatar answered Jan 03 '23 16:01

TimoStaudinger


This is kind of late to answer but I couldn't find any official or better ways to assign default props that way so that the props would be merged with the defaults!

I did it in my own way as shown below:

  1. Define a defaultProps variable before the rendering function or class
  2. Use
const var = Object.assign({}, defaultProps.var , props.var);

to get the props object var merged with the default variable var.

Of course, you should do a little more coding for nested objects and arrays to make this approach go well! the assign function is for shallow merging.

The best approach is using _.merge function of *loadash library.

  1. Assign App.propTypes and App.defaultProps

The complete example

import React from 'react';
import PropTypes from 'prop-types';

const defaultProps = {
  user: {
    id: 0,
    name: 'Mike',
    family: 'wheeler',
  },
  address: {
    addr1: 'Saint Louis, Arsenal Street',
    addr2: '',
    tel: null,
  },
  color: 'orange',
  weight: 60,
  age: 20,
};

.
.
.

export default function App(props){
  const {color, weight, age} = props;
  const user = Object.assign({}, defaultProps.user , props.user);
  const address = Object.assign({}, defaultProps.address, props.address);

  return (<div></div>);
}

App.propTypes = {
  type: PropTypes.string.isRequired,
  user: PropTypes.shape({
    id: PropTypes.number,
    name: PropTypes.string,
    family: PropTypes.string,
  }),
  address: PropTypes.shape({
    addr1: PropTypes.string,
    addr2: PropTypes.string,
    tel: PropTypes.string,
  }),
  color: PropTypes.string,
  weight: PropTypes.number,
  age: PropTypes.number,
};

App.defaultProps = defaultProps;

like image 35
Alireza Kavian Avatar answered Jan 03 '23 18:01

Alireza Kavian