Ответ 1
В общем случае вы хотите, чтобы только компоненты контейнера верхнего уровня имели доступ к хранилищу - они передадут любые необходимые данные или диспетчеризацию действий в качестве реквизитов для своих дочерних компонентов. В этом разница между "умным" и "тупым" компонентами - "умные" компоненты знают о хранилище/состоянии Redux, в то время как "немые" компоненты просто передают им реквизит и не имеют представления о большем состоянии приложения.
Тем не менее, даже просто передать хранилище на компоненты контейнера может стать утомительным. Вот почему React-Redux предоставляет один компонент из коробки, который оборачивает все ваше приложение. Проверьте это в документах. Это компонент Provider
и когда вы оборачиваете им все свое приложение, вы передаете хранилище компоненту только один раз:
import createStore from '../store';
const store = createStore()
class App extends Component {
render() {
return (
<Provider store={store}>
<MainAppContainer />
</Provider>
)
}
}
Как вы можете видеть здесь, у меня есть отдельный файл конфигурации только для моего магазина, так как вы можете сделать много изменений и для любого удаленно сложного приложения, вы обнаружите, что делаете то же самое для таких вещей, как использование compose для применения промежуточного программного обеспечения.
Тогда любой из ваших оставшихся "умных" компонентов (обычно обертки) необходимо прослушать в магазине. Это достигается с помощью метода подключения. Это позволяет вам отображать части состояния в свойствах вашего компонента, а также отправлять действия как свойства.
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from './actionCreators';
const mapStateToProps = function(state){
return {
something: state.something,
}
}
const mapDispatchToProps = function (dispatch) {
return bindActionCreators({
getSomething: actionCreators.getSomething,
}, dispatch)
}
class MainAppContainer extends Component {
componentDidMount() {
//now has access to data like this.props.something, which is from store
//now has access to dispatch actions like this.props.getSomething
}
render() {
//will pass down store data and dispatch actions to child components
return (
<div>
<ChildComponent1 something={this.props.something} />
<ChildComponent2 getSomething={this.props.getSomething} />
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MainAppContainer)
Поскольку вы всегда передаете действия и данные диспетчеризации своему дочернему компоненту как свойства, вы просто ссылаетесь на эти компоненты с помощью this.props
.
ChildComponent1
приведенный выше пример, вы увидите, что, поскольку я передал this.props.something
в ChildComponent1
, у него есть доступ к данным something
из хранилища, но у него нет доступа к getSomething
диспетчеризации getSomething
. Аналогично, ChildComponent2
имеет доступ только к диспетчерскому действию getSomething
но не к данным something
. Это означает, что вы выставляете компоненты только на то, что им нужно из магазина.
Например, поскольку ChildComponent2
был передан в диспетчерское действие как getSomething
, в моем onClick
я могу вызвать this.props.getSomething
и он вызовет диспетчерское действие без необходимости какого-либо доступа к хранилищу. Таким же образом он может продолжать передавать getSomething
другому дочернему компоненту, и этот компонент может вызывать его и/или передавать его вниз, и цикл может продолжаться бесконечно.
class ChildComponent2 extends Component {
render() {
return (
<div>
<div onClick={this.props.getSomething}>Click me</div>
<NestedComponent getSomething={this.props.getSomething} />
</div>
)
}
}
Редактировать из комментариев
Хотя это не относится непосредственно к вопросу, в комментариях вы были немного смущены действиями. Я фактически не определял действие getSomething
здесь. Вместо этого в приложениях Redux обычно все ваши определения действий помещаются в отдельный файл с именем actionCreators.js
. Он содержит функции, которые называются так же, как ваши действия, и возвращают объект со свойством type
и любые другие методы/данные, необходимые для действия. Например, вот очень простой пример файла actionCreators.js
:
export function getSomething() {
return {
type: 'GET_SOMETHING',
payload: {
something: 'Here is some data'
}
}
}
Этот тип действия - то, что ваш редуктор будет слушать, чтобы узнать, какое действие было запущено.