Анализ вложенных записей в Immutable.js

Предположим, что у меня есть следующие записи, определенные с помощью Immutable.js:

var Address = Immutable.Record({street: '', city: '', zip: ''});
var User = Immutable.Record({name: '', address: new Address()});

Как преобразовать простой объект javascript в запись пользователя? Я попробовал следующее, но он не дает ожидаемого результата:

var user = new User({name: 'Foo', address: {street: 'Bar', city: 'Baz'}});
// => Record { "name": "Foo", "address": [object Object] }

Я знаю, что можно явно создать запись адреса:

var user = new User({name: 'Foo', address: new Address({street: 'Bar', city: 'Baz'})});
// => Record { "name": "Foo", "address": Record { "street": "Bar", "city": "Baz", "zip": "" } }

Но это не решение, которое я ищу. Представьте, что у вас есть записи, вложенные на несколько уровней в глубину и вы хотите хранить/извлекать данные как JSON (например, в базе данных). Я хотел бы использовать фактическую структуру записей пользователя как информацию схемы для воссоздания вложенных записей. Или есть лучший способ представления вложенных и структурированных неизменяемых данных?

Ответы

Ответ 1

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

Итак, используя ваш пример, если вы инициализируете запись без предоставления адреса, вы получите соответствующий объект Immutable.Record для адреса:

var user = new User({name: 'Foo'});
// => Record { "name": "Foo", "address": Record { "street": "", "city": "", "zip": "" } }

Одним из хакерских способов добиться того, что вы хотите, было бы написать обертку по методу Immutable.fromJS с пользовательской функцией reviver:

Immutable.Record.constructor.prototype.fromJS = function(values) {
  var that = this;
  var nested = Immutable.fromJS(values, function(key, value){
    if(that.prototype[key] && that.prototype[key].constructor.prototype instanceof Immutable.Record){return that.prototype[key].constructor.fromJS(value)}
    else { return value }
  });
  return this(nested);
}

Затем вы можете использовать его следующим образом:

var user = User.fromJS({name: 'Foo', address: {street: 'Bar', city: 'Baz'}});

// => User { "name": "Foo", "address": Record { "street": "Bar", "city": "Baz", "zip": "" } }

Однако, если вы хотите иметь правильную проверку своих структур данных, я бы рекомендовал использовать Immutable.js вместе с некоторым статическим контролером типа, например http://flowtype.org/ или http://www.typescriptlang.org/

Ответ 2

Вы можете сделать User подклассом определение Record и проанализировать address в конструкторе:

import {Record} from 'immutable'

const Address = Record({street: '', city: '', zip: ''});
class User extends Record({name: '', address: new Address()}) {
  constructor({name, address} = {}) {
    super({name, address: new Address(address)})
  }
}

const user = new User({
  name: 'Andy',
  address: {
    street: 'wherever',
    city: 'Austin',
    zip: 'TX'
  }
})

console.log(user)
console.log('user.address instanceof Address:', user.address instanceof Address)

const user2 = new User({name: 'Bob'})

console.log(user2)
console.log('user2.address instanceof Address:', user2.address instanceof Address)

Вывод:

User { "name": "Andy", "address": Record { "street": "wherever", "city": "Austin", "zip": "TX" } }
user.address instanceof Address: true
User { "name": "Bob", "address": Record { "street": "", "city": "", "zip": "" } }
user2.address instanceof Address: true