Как правильно передать вложенные свойства с атрибутами распространения? (JSX)

# 1

Привет. У меня есть код:

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"

Почему я получаю 1 ссылку на переменную для 2 отдельных компонентов вместо 2 экземпляров nested.prop для каждого компонента? Почему this.props сохраняет только последнее установленное значение для всех экземпляров компонента после монтирования? Это нормальное поведение? Я думаю, что правильное поведение имеет разные значения свойств для разных экземпляров.

Постскриптум Я проверил этот код здесь.

# 2

jimfb получил ответ: "You are mutating the default prop that was passed in. The line this.props.nested.prop = this.props.parse.nested.prop; is illegal."

Мой следующий вопрос: How to pass nested properties without a manual mutation of props?

Например:

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

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

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

Руководство к приведенному выше коду: функция атрибута распространения JSX просто переопределяет props.nested, и я теряю вложенные свойства по умолчанию. Но это не то, что мне нужно. Как насчет реализации рекурсивного обхода вложенных объектов на этапе анализа атрибутов распространения JSX?
Или есть какой-то полезный шаблон для этого случая?

Ответ 1

Это действительно хороший вопрос!

Короткий ответ: вы не можете делать глубокое слияние с оператором спредов - он только делает мелкое слияние. Но вы, безусловно, можете написать функцию, которая будет выполнять объекты, пересекающие и реализующие глубокое слияние.

Это фактически оставляет вам 3 варианта:

1) Только не делайте глубокого слияния. Если у вас есть 2-уровневый вложенный объект, вы можете сделать такую ​​простую вещь:

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

Неверное слияние сила, чтобы вы сказали явно, что у вас будет новое значение вложенного свойства. Это хорошо, потому что это делает ваш код очевидным. Вы также можете попробовать запустить приведенные примеры здесь.

2) Вы можете использовать библиотеку для неизменяемых структур. F.E. immutable.js. С ним ваш код будет выглядеть довольно похоже.

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) Вы можете использовать глубокое слияние: найти некоторую реализацию в npm или написать ее самостоятельно и у вас будет такой код (в качестве примера - пример immutable.js):

const finalProps = oldProps.mergeDeep(newProps);

В некоторых случаях это может потребоваться, но такой код делает операцию обновления неявной, и в ней возникает много проблем, которые перечислены значительно здесь