如何在 Redux 中为每个实例创建一个商店?
How to create one store per instance in Redux?
有时在 Redux 应用程序中为每个实例创建一个存储会很有用。 Redux 的创建者自己创建了一个 Gist,描述了如何实现这一点:https://gist.github.com/gaearon/eeee2f619620ab7b55673a4ee2bf8400
我已经在 Gist 中问过这个问题,但我认为 Whosebug 是解决这个问题的更好地方:
不知如何dispatch
对组件自身的特殊存储进行操作?有没有办法为每个 <SubApp />
(及其子组件)访问 <Provider />
的 store
-prop?
例如:我有一些 API 类 在从远程服务器获取数据后调用 dispatch
。但是由于我无法导入 "normal" 商店,处理自定义商店以使其可用于其他 classes/files/services 的最佳方式是什么?
更新 1
所以我让它工作了,但我认为这是一种非常肮脏的方式(注意代码中的 UGLY?
注释):
提供商:
通过在构造函数中创建商店来为每个实例创建一个商店:
export default class OffersGridProvider extends React.Component {
constructor(props) {
super(props)
this.store = createStore(reducers);
}
render() {
return (
<Provider store={this.store}>
<OffersGridContainer offers={this.props.offers} />
</Provider>
);
}
}
容器:
Provider 为 this store 注入了一个 dispatch
方法到我的 OffersGridContainer
中,我可以用它来将操作调度到这个实例的 store:
class OffersGridContainer extends React.Component {
componentDidMount() {
// UGLY???
const { dispatch } = this.props;
let destinationIds = [];
this.props.offers.forEach((offer) => {
offer.to.forEach((destination) => {
destinationIds.push(destination.destination);
});
});
// MORE UGLY???
destinationsApi.getDestinations(dispatch, destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
export default connect(mapStateToProps)(OffersGridContainer);
API方法:
只需使用我作为参数传递给 API 方法的 dispatch
方法:
export function getDestinations(dispatch, ids) {
const url = $('meta[name="site-url"]').attr('content');
const filter = ids.map((id) => {
return `filter[post__in][]=${id}`;
}).join('&');
return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`)
.then(response => {
dispatch(getOffersGridSuccess(response.data));
return response;
});
}
更新 2
刚刚在评论中收到关于 mapDispatchToProps
的提示,所以我的 Container
现在看起来像这样:
class OffersGridContainer extends React.Component {
componentDidMount() {
let destinationIds = [];
this.props.offers.forEach((offer) => {
offer.to.forEach((destination) => {
destinationIds.push(destination.destination);
});
});
this.props.getDestinations(destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
const mapDispatchToProps = function(dispatch) {
return {
getDestinations: function(ids) {
return destinationsApi.getDestinations(dispatch, ids);
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);
更新三(最终答案)
现在一切正常!这是代码:
提供商:
export default class OffersGridProvider extends React.Component {
constructor(props) {
super(props)
this.store = createStore(reducers, applyMiddleware(thunk));
}
render() {
return (
<Provider store={this.store}>
<OffersGridContainer offers={this.props.offers} />
</Provider>
);
}
}
容器:
class OffersGridContainer extends React.Component {
componentDidMount() {
const destinationIds = this.props.offers.reduce((acc, offer) => {
return [...acc, ...offer.to.map(d => d.destination)];
}, []);
this.props.getDestinations(destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
const mapDispatchToProps = function(dispatch) {
return {
getDestinations: function(ids) {
return dispatch(destinationsApi.getDestinations(ids));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);
API方法:
export function getDestinations(ids) {
return function(dispatch) {
const url = $('meta[name="site-url"]').attr('content');
const filter = ids.map((id) => {
return `filter[post__in][]=${id}`;
}).join('&');
return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`)
.then(response => {
return dispatch(getOffersGridSuccess(response.data));
});
}
}
与其直接在组件中进行 api 调用,我建议您使用 redux-thunk
包。
接下来,您应该将 mapDispatchToProps
函数作为第二个参数传递给 connect
函数,以将动作创建器函数注入组件:
import { getDestinations } from '../actions';
class OffersGridContainer extends React.Component {
componentDidMount() {
// Tip.
const destinationIds = this.props.offers.reduce((acc, offer) => {
return [...acc, ...offer.to.map(d => d.destination)];
}, []);
// instead `destinationsApi.getDestinations(dispatch, destinationIds)`
// call action creator function
this.props.getDestinations(destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
const mapDispatchToProps = function(dispatch) {
return {
// this function will be available in component as
// `this.props.getDestinations`.
getDestinations: function(destinationIds) {
dispatch(getDestinations(destinationIds));
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);
有时在 Redux 应用程序中为每个实例创建一个存储会很有用。 Redux 的创建者自己创建了一个 Gist,描述了如何实现这一点:https://gist.github.com/gaearon/eeee2f619620ab7b55673a4ee2bf8400
我已经在 Gist 中问过这个问题,但我认为 Whosebug 是解决这个问题的更好地方:
不知如何dispatch
对组件自身的特殊存储进行操作?有没有办法为每个 <SubApp />
(及其子组件)访问 <Provider />
的 store
-prop?
例如:我有一些 API 类 在从远程服务器获取数据后调用 dispatch
。但是由于我无法导入 "normal" 商店,处理自定义商店以使其可用于其他 classes/files/services 的最佳方式是什么?
更新 1
所以我让它工作了,但我认为这是一种非常肮脏的方式(注意代码中的 UGLY?
注释):
提供商:
通过在构造函数中创建商店来为每个实例创建一个商店:
export default class OffersGridProvider extends React.Component {
constructor(props) {
super(props)
this.store = createStore(reducers);
}
render() {
return (
<Provider store={this.store}>
<OffersGridContainer offers={this.props.offers} />
</Provider>
);
}
}
容器:
Provider 为 this store 注入了一个 dispatch
方法到我的 OffersGridContainer
中,我可以用它来将操作调度到这个实例的 store:
class OffersGridContainer extends React.Component {
componentDidMount() {
// UGLY???
const { dispatch } = this.props;
let destinationIds = [];
this.props.offers.forEach((offer) => {
offer.to.forEach((destination) => {
destinationIds.push(destination.destination);
});
});
// MORE UGLY???
destinationsApi.getDestinations(dispatch, destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
export default connect(mapStateToProps)(OffersGridContainer);
API方法:
只需使用我作为参数传递给 API 方法的 dispatch
方法:
export function getDestinations(dispatch, ids) {
const url = $('meta[name="site-url"]').attr('content');
const filter = ids.map((id) => {
return `filter[post__in][]=${id}`;
}).join('&');
return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`)
.then(response => {
dispatch(getOffersGridSuccess(response.data));
return response;
});
}
更新 2
刚刚在评论中收到关于 mapDispatchToProps
的提示,所以我的 Container
现在看起来像这样:
class OffersGridContainer extends React.Component {
componentDidMount() {
let destinationIds = [];
this.props.offers.forEach((offer) => {
offer.to.forEach((destination) => {
destinationIds.push(destination.destination);
});
});
this.props.getDestinations(destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
const mapDispatchToProps = function(dispatch) {
return {
getDestinations: function(ids) {
return destinationsApi.getDestinations(dispatch, ids);
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);
更新三(最终答案)
现在一切正常!这是代码:
提供商:
export default class OffersGridProvider extends React.Component {
constructor(props) {
super(props)
this.store = createStore(reducers, applyMiddleware(thunk));
}
render() {
return (
<Provider store={this.store}>
<OffersGridContainer offers={this.props.offers} />
</Provider>
);
}
}
容器:
class OffersGridContainer extends React.Component {
componentDidMount() {
const destinationIds = this.props.offers.reduce((acc, offer) => {
return [...acc, ...offer.to.map(d => d.destination)];
}, []);
this.props.getDestinations(destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
const mapDispatchToProps = function(dispatch) {
return {
getDestinations: function(ids) {
return dispatch(destinationsApi.getDestinations(ids));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);
API方法:
export function getDestinations(ids) {
return function(dispatch) {
const url = $('meta[name="site-url"]').attr('content');
const filter = ids.map((id) => {
return `filter[post__in][]=${id}`;
}).join('&');
return axios.get(`${url}/wp-json/wp/v2/destinations?filter[posts_per_page]=-1&${filter}`)
.then(response => {
return dispatch(getOffersGridSuccess(response.data));
});
}
}
与其直接在组件中进行 api 调用,我建议您使用 redux-thunk
包。
接下来,您应该将 mapDispatchToProps
函数作为第二个参数传递给 connect
函数,以将动作创建器函数注入组件:
import { getDestinations } from '../actions';
class OffersGridContainer extends React.Component {
componentDidMount() {
// Tip.
const destinationIds = this.props.offers.reduce((acc, offer) => {
return [...acc, ...offer.to.map(d => d.destination)];
}, []);
// instead `destinationsApi.getDestinations(dispatch, destinationIds)`
// call action creator function
this.props.getDestinations(destinationIds);
}
render() {
return (
<OffersGridLayout destinations={this.props.destinations} />
);
}
}
const mapStateToProps = function(store) {
return {
destinations: store.offersGridState.destinations
};
}
const mapDispatchToProps = function(dispatch) {
return {
// this function will be available in component as
// `this.props.getDestinations`.
getDestinations: function(destinationIds) {
dispatch(getDestinations(destinationIds));
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(OffersGridContainer);