Ответ 1
Похоже, вы погрузились в использование Redux, не получив при этом твердого сопротивления React. Я бы не рекомендовал делать это, потому что теперь вы немного смущены.
Мышление в действии - отличное руководство, и я рекомендую вам сначала пройти его и устроиться с идеей государственной собственности в React, прежде чем использовать Redux.
В React вещи, которые меняются с течением времени ( "состояние" ), всегда "принадлежат" некоторым компонентам. Иногда это тот же компонент, который использует это состояние для рендеринга. Иногда многие компоненты должны быть синхронизированы, поэтому состояние получает "подъем" к некоторому родительскому компоненту, который может управлять ими всеми, и передавать эту информацию через реквизиты. Всякий раз, когда изменяется состояние, все они обновляются.
Важная часть здесь состоит в том, что props
предполагается получить родителем. Если компонент хочет изменить свои собственные реквизиты, это является симптомом проблемы:
- Либо вы должны были использовать
state
для этого компонента, поэтому вы можете вызватьsetState
- Или ваш компонент должен получить доступ к обратному вызову, например
onChange
, чтобы он мог "спросить" значение, которое нужно изменить.
В первом случае ваш код будет выглядеть так, и он будет действительным. React:
export default React.createClass({
getInitialState: function() {
return { activeTab: 0 }
},
render: function() {
return (
<div className="demo-tabs">
<Tabs activeTab={this.state.activeTab}
onChange={(tabId) => this.setState({ activeTab: tabId })} ripple>
<Tab>Stack</Tab>
<Tab>GitHub</Tab>
<Tab>Twitter</Tab>
</Tabs>
<section>
<div className="content">Content for the tab: {this.state.activeTab}</div>
</section>
</div>
);
}
});
Однако вы можете продолжить шаблон, который вы уже используете с компонентом <Tabs>
внутри, и еще больше повышать состояние. Тогда вашему компоненту нужно будет принять опору onChange
, которая будет передаваться до <Tabs>
. Теперь он не знает, как состояние обновляется, и не сохраняет состояние:
export default React.createClass({
render: function() {
return (
<div className="demo-tabs">
<Tabs activeTab={this.props.activeTab}
onChange={this.props.onChange} ripple>
<Tab>Stack</Tab>
<Tab>GitHub</Tab>
<Tab>Twitter</Tab>
</Tabs>
<section>
<div className="content">Content for the tab: {this.props.activeTab}</div>
</section>
</div>
);
}
});
Ни один подход не лучше или хуже, они используются для разных целей. Первый из них более удобен, когда состояние не имеет отношения к остальной части приложения, второе удобно, когда ему нужны другие удаленные компоненты. Убедитесь, что вы понимаете компромиссы обоих подходов.
Даже при втором подходе, что-то заняло состояние. Это может быть другой компонент, или (и в этом заключается Redux) это может быть отдельное хранилище данных, такое как хранилище Redux. В этом случае вместо того, чтобы поставлять onChange
из родительского компонента, вы должны использовать connect()
для привязки onChange
prop, который он вставляет для отправки действия Redux:
const mapStateToProps = (state) => {
return {
activeTabId: state.activeTabId
}
}
const mapDispatchToProps = (dispatch) => {
return {
onChange: (tabId) => dispatch({ type: 'SET_ACTIVE_TAB', tabId })
}
}
И в ваших редукторах вы можете справиться с этим действием:
const activeTabId = (state = 0, action) => {
switch (action.type) {
case 'SET_ACTIVE_TAB':
return action.tabId
default:
return state
}
}
const reducer = combineReducers({
activeTabId,
// other reducers
})
const store = createStore(reducer)
В любом случае нам нужно setProps
. Вы можете:
- пусть компонент владеет состоянием и использует
setState
, - пусть он принимает реквизиты
activeTabId
иonChange
и управляет ими из компонента, выше в дереве, который будет использоватьsetState
, или - вы можете полностью переместить обработку состояния из компонентов в нечто вроде Redux, но ваши компоненты все равно будут принимать
activeTabId
иonChange
как реквизиты.