2014-08-16 5 views
81

Я по существу пытаюсь внести вкладки в реакцию, но с некоторыми проблемами.Reactjs: как изменить динамическое состояние дочернего компонента или реквизит от родителя?

Вот файл page.jsx

<RadioGroup> 
    <Button title="A" /> 
    <Button title="B" /> 
</RadioGroup> 

При нажатии на кнопку А, компонент RadioGroup нужно отменить выбор кнопки B.

«Selected» просто означает Classname из состояния или свойства

Вот RadioGroup.jsx:

module.exports = React.createClass({ 

    onChange: function(e) { 
     // How to modify children properties here??? 
    }, 

    render: function() { 
     return (<div onChange={this.onChange}> 
      {this.props.children} 
     </div>); 
    } 

}); 

Источник действительно не имеет значения, то есть кнопка регулярные HTML радио, который вызывает родной DOM onChange событие

ожидаемый поток:

  • Нажмите на кнопку "A"
  • Кнопка "A" вызывает OnChange, родное событие DOM, который пузырится до RadioGroup
  • RadioGroup OnChange слушатель называется
  • RadioGroup необходимо отменить выбор кнопки B , Это мой вопрос.

Вот главная проблема, я встречая: Я не может двигаться <Button> сек в RadioGroup, потому что структура этого такова, что дети произвольное. То есть, разметка может быть

<RadioGroup> 
    <Button title="A" /> 
    <Button title="B" /> 
</RadioGroup> 

или

<RadioGroup> 
    <OtherThing title="A" /> 
    <OtherThing title="B" /> 
</RadioGroup> 

Я попробовал несколько вещей.

Попытка: В RadioGroup 's OnChange обработчика:

React.Children.forEach(this.props.children, function(child) { 

    // Set the selected state of each child to be if the underlying <input> 
    // value matches the child's value 

    child.setState({ selected: child.props.value === e.target.value }); 

}); 

Проблема:

Invalid access to component property "setState" on exports at the top 
level. See react-warning-descriptors . Use a static method 
instead: <exports />.type.setState(...) 

Попытка: В RadioGroup' обработчик OnChange s:

React.Children.forEach(this.props.children, function(child) { 

    child.props.selected = child.props.value === e.target.value; 

}); 

Проблема: Ничего не происходит, даже я даю Button класс это метод componentWillReceiveProps


Попытка: Я попытался пройти некоторое определенное состояние родителя к детям, поэтому я может просто обновить родительское состояние и дать детям ответ автоматически. В зависимости от RadioGroup визуализации:

React.Children.forEach(this.props.children, function(item) { 
    this.transferPropsTo(item); 
}, this); 

Проблема:

Failed to make request: Error: Invariant Violation: exports: You can't call 
transferPropsTo() on a component that you don't own, exports. This usually 
means you are calling transferPropsTo() on a component passed in as props 
or children. 

Bad решение # 1: Использование реагировать-addons.js cloneWithProps метод клонирования детей во время визуализации в RadioGroup для того, чтобы передать им свои свойства

Bad решение # 2: Реализовать абстракцию вокруг HTML/JSX, так что я могу передать в свойствах динамически (убить меня):

<RadioGroup items=[ 
    { type: Button, title: 'A' }, 
    { type: Button, title: 'B' } 
]; /> 

А потом в RadioGroup динамически создавать эти кнопки.

This question мне не помогает, потому что мне нужно сделать мои дети, не зная, что они

+0

Если дети могут быть произвольными, то как «Радиогруппа», возможно, знает, что ей нужно реагировать на случай произвольных детей? Он обязательно должен знать что-то о своих детях. –

+1

В общем случае, если вы хотите изменить свойства компонента, которого у вас нет, клонируйте его, используя ['React.addons.cloneWithProps'] (https://facebook.github.io/react/docs/clone-with -props.html) и передать новые реквизиты, которые вы хотели бы иметь. 'props' неизменяемы, поэтому вы создаете новый хеш, объединяете его с текущими реквизитами и передаете новый хэш как« реквизит »в новый экземпляр дочернего компонента. –

ответ

35

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

var Hello = React.createClass({ 
    render: function() { 
     return <div>Hello {this.props.name}</div>; 
    } 
}); 

var App = React.createClass({ 
    render: function() { 
     return (
      <Group ref="buttonGroup"> 
       <Button key={1} name="Component A"/> 
       <Button key={2} name="Component B"/> 
       <Button key={3} name="Component C"/> 
      </Group> 
     ); 
    } 
}); 

var Group = React.createClass({ 
    getInitialState: function() { 
     return { 
      selectedItem: null 
     }; 
    }, 

    selectItem: function(item) { 
     this.setState({ 
      selectedItem: item 
     }); 
    }, 

    render: function() { 
     var selectedKey = (this.state.selectedItem && this.state.selectedItem.props.key) || null; 
     var children = this.props.children.map(function(item, i) { 
      var isSelected = item.props.key === selectedKey; 
      return React.addons.cloneWithProps(item, { 
       isSelected: isSelected, 
       selectItem: this.selectItem, 
       key: item.props.key 
      }); 
     }, this); 

     return (
      <div> 
       <strong>Selected:</strong> {this.state.selectedItem ? this.state.selectedItem.props.name : 'None'} 
       <hr/> 
       {children} 
      </div> 
     ); 
    } 

}); 

var Button = React.createClass({ 
    handleClick: function() { 
     this.props.selectItem(this); 
    }, 

    render: function() { 
     var selected = this.props.isSelected; 
     return (
      <div 
       onClick={this.handleClick} 
       className={selected ? "selected" : ""} 
      > 
       {this.props.name} ({this.props.key}) {selected ? "<---" : ""} 
      </div> 
     ); 
    } 

}); 


React.renderComponent(<App />, document.body); 

jsFiddle показать его в действии.

EDIT: вот более полный пример с динамическим содержимым вкладки: jsFiddle

+11

Примечание: cloneWithProps устарел. Вместо этого используйте React.cloneElement. –

+5

Это настолько невероятно сложно сделать что-то, что D3/Jquery может сделать только с помощью: не селектора на 'this'. Есть ли реальные преимущества, кроме использования React? –

+0

FYI .. это не «обновление состояния детей из родительского состояния», это дочернее обновление родительского состояния для обновления состояния детей. Здесь есть разница в вербальном выражении. Если это смущает людей. – sksallaj

13

Кнопки должны быть лицами без гражданства. Вместо того, чтобы явно обновлять свойства кнопки, просто обновите файл группы. состояния и повторной обработки. Затем метод рендеринга группы должен посмотреть свое состояние при рендеринге кнопок и передать «активную» (или что-то) только активную кнопку.

5

Возможно, мое странное решение, но почему бы не использовать шаблон наблюдателя?

RadioGroup.jsx

module.exports = React.createClass({ 
buttonSetters: [], 
regSetter: function(v){ 
    buttonSetters.push(v); 
}, 
handleChange: function(e) { 
    // ... 
    var name = e.target.name; //or name 
    this.buttonSetters.forEach(function(v){ 
     if(v.name != name) v.setState(false); 
    }); 
}, 
render: function() { 
    return (
    <div> 
     <Button title="A" regSetter={this.regSetter} onChange={handleChange}/> 
     <Button title="B" regSetter={this.regSetter} onChange={handleChange} /> 
    </div> 
); 
}); 

Button.jsx

module.exports = React.createClass({ 

    onChange: function(e) { 
     // How to modify children properties here??? 
    }, 
    componentDidMount: function() { 
     this.props.regSetter({name:this.props.title,setState:this.setState}); 
    }, 
    onChange:function() { 
     this.props.onChange(); 
    }, 
    render: function() { 
     return (<div onChange={this.onChange}> 
      <input element .../> 
     </div>); 
    } 

}); 

может быть, вам требуется что-то еще, но я нашел, что это очень мощный,

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

+0

Разве это не плохая форма для того, чтобы узел явно вызывал setState на другом узле? Было бы более реактивным способом обновить какое-то состояние в RadioGroup, вызвав другой шаг рендеринга, в котором вы могли бы обновить опоры потомков по мере необходимости? – qix

+1

Явно? Нет, наблюдатель просто вызывает обратный вызов, это, скорее всего, setState, но на самом деле я мог бы указать this.setState.bind (это) и поэтому быть исключительно привязанным к себе. Хорошо, если бы я был честен, я должен был определить локальную функцию/метод, который вызывает setState и зарегистрировал его в наблюдателе. –

+1

Я читаю это неправильно или у вас есть два поля с тем же именем в одном объекте для кнопки. jsx'? – JohnK

Смежные вопросы