React.js 2-сторонние привязки: двухуровневый глубокий путь в valueLink
Мое состояние:
[
{type: "translateX", x: 10},
{type: "scaleX", x: 1.2}
]
Я использую Двусторонние помощники привязки, и я не могу предоставить действительную строку ключа для linkState
:
this.state.map(function(item, i) {
return <div><input valueLink={this.linkState( ??? )}></div>
}
Было бы хорошо, если бы this.linkState
принял некоторый синтаксис запроса, например "0.type"
, чтобы извлечь "translateX"
из моего примера.
Есть ли способы обхода?
Я написал DeepLinkState mixin, который является заменой для React.addons.LinkedStateMixin. Пример использования:
this.state.map(function(item, i) {
return <div><input valueLink={this.linkState([i, "x"])}></div>
}
linkState("0.x")
также является приемлемым синтаксисом.
Ответы
Ответ 1
Изменить:
Я понял, что глубокий путь для LinkedState
довольно крут, поэтому я пытаюсь его реализовать.
Код: https://gist.github.com/tungd/8367229
Использование: http://jsfiddle.net/uHm6k/3/
Как указано в документе, LinkedState
является оберткой вокруг onChange/setState
и предназначен для простого случая. Вы всегда можете написать полный onChange/setState
для достижения желаемого. Если вы действительно хотите придерживаться LinkedState
, вы можете использовать версию non mixin, например:
getInitialState: function() {
return { values: [
{ type: "translateX", x: 10 },
{ type: "scaleX", x: 1.2 }
]}
},
handleTypeChange: function(i, value) {
this.state.values[i].type = value
this.setState({ values: this.state.values })
},
render: function() {
...
this.state.values.map(function(item, i) {
var typeLink = {
value: this.state.values[i].type,
requestChange: this.handleTypeChange.bind(null, i)
}
return <div><input valueLink={typeLink}/></div>
}, this)
...
}
Здесь работает JSFiddle: http://jsfiddle.net/srbGL/
Ответ 2
Вы можете реализовать свой собственный mixin, если базовый mixin не удовлетворяет вас.
Посмотрите, как реализован этот mixin:
var LinkedStateMixin = {
/**
* Create a ReactLink that linked to part of this component state. The
* ReactLink will have the current value of this.state[key] and will call
* setState() when a change is requested.
*
* @param {string} key state key to update. Note: you may want to use keyOf()
* if you're using Google Closure Compiler advanced mode.
* @return {ReactLink} ReactLink instance linking to the state.
*/
linkState: function(key) {
return new ReactLink(
this.state[key],
ReactStateSetters.createStateKeySetter(this, key)
);
}
};
/**
* @param {*} value current value of the link
* @param {function} requestChange callback to request a change
*/
function ReactLink(value, requestChange) {
this.value = value;
this.requestChange = requestChange;
}
https://github.com/facebook/react/blob/fc73bf0a0abf739a9a8e6b1a5197dab113e76f27/src/addons/link/LinkedStateMixin.js
https://github.com/facebook/react/blob/fc73bf0a0abf739a9a8e6b1a5197dab113e76f27/src/addons/link/ReactLink.js
Таким образом, вы можете легко попытаться написать свою собственную функцию linkState
на основе вышеописанного.
linkState: function(key,key2) {
return new ReactLink(
this.state[key][key2],
function(newValue) {
this.state[key][key2] = newValue;
}
);
}
Обратите внимание, что я не использовал ReactStateSetters.createStateKeySetter(this, key)
.
https://github.com/facebook/react/blob/fc73bf0a0abf739a9a8e6b1a5197dab113e76f27/src/core/ReactStateSetters.js
Если снова взглянуть на исходный код, вы можете узнать, что этот метод не делает так много, кроме того, что он создает функцию и мало оптимизирует кеширование:
function createStateKeySetter(component, key) {
// Partial state is allocated outside of the function closure so it can be
// reused with every call, avoiding memory allocation when this function
// is called.
var partialState = {};
return function stateKeySetter(value) {
partialState[key] = value;
component.setState(partialState);
};
}
Итак, вы обязательно должны попытаться написать свой собственный mixin.
Это может быть очень полезно, если у вас в вашем состоянии сложный объект, и вы хотите его изменить через API-интерфейс объекта.
Ответ 3
Я делаю это без использования addon-ссылки.
Вот демо: http://wingspan.github.io/wingspan-forms/examples/form-twins/
Секретный соус должен определять только одну функцию onChange:
onChange: function (path, /* more paths,*/ value) {
// clone the prior state
// traverse the tree by the paths and assign the value
this.setState(nextState);
}
используйте его следующим образом:
<input
value={this.state['forms']['0']['firstName']}
onChange={_.partial(this.onChange, 'forms', '0', 'firstName')} />
Если у вас много пар (value, onChange)
, которые вам нужно обойти повсюду, может возникнуть смысл определить абстракцию вокруг этого, похожего на ReactLink, но я лично довольно далеко не использовал ReactLink.
Недавно мои коллеги открыли источник wingspan-forms, библиотеку React, которая помогает с глубоко вложенным состоянием. Мы широко используем этот подход. Вы можете увидеть больше примеров демонстраций со связанным состоянием на странице github.
Ответ 4
Я написал blogpost об этом: http://blog.sendsonar.com/2015/08/04/angular-like-deep-path-data-bindings-in-react/
Но в основном я создал новый компонент, который принял бы "состояние" родителя и глубокий путь, поэтому вам не нужно писать дополнительный код.
<MagicInput binding={[this, 'account.owner.email']} />
Там тоже есть JSFiddle, поэтому вы можете играть с ним
Ответ 5
Вот учебник, объясняющий, как обращаться с такими вещами.
Состояние и формы в действии, часть 3: Обработка сложного состояния
TL; ДР:
0) Не используйте стандартные ссылки. Используйте эти.
1) Измените свое состояние так:
collection : [
{type: "translateX", x: 10},
{type: "scaleX", x: 1.2}
]
2) Возьмите ссылку на collection
:
var collectionLink = Link.state( this, 'collection' );
3) Итерации по ссылкам на его элементы:
collectionLink.map(function( itemLink, i ) {
return <div><input valueLink={itemLink}></div>
})
Ответ 6
Я принял другой подход, который не использует mixins и не автоматически мутирует состояние
См. github.com/mcmlxxxviii/react-value-link