在Redux中,我可以使用
轻松订阅商店更改store.subscribe(() => my handler goes here)
但是,如果我的商店里装满了不同的对象,并且在我的应用中的某个特定位置,我想订阅仅在商店中的特定对象中进行的更改?
答案 0 :(得分:81)
直接使用subscribe
时无法订阅商店的一部分,但正如Redux的创建者所说的那样 - don't use subscribe
directly!为了使Redux应用程序的数据流真正起作用,将需要一个包装整个应用程序的组件。该组件将订阅您的商店。其余组件将成为此包装器组件的子组件,并且只能获得所需状态的部分。
如果您使用Redux和React,那么有好消息 - 官方react-redux包为您解决此问题!它提供了包装组件,称为<Provider />
。然后,您将拥有至少一个智能组件&#34;侦听来自商店的Provider
传递的状态更改。您可以指定它应该监听的状态的哪些部分,并且状态的那些部分将作为道具传递给该组件(当然,它可以将这些部分传递给它自己的子组件)。您可以使用&#34; smart&#34;上的connect()功能指定该功能。组件并使用mapStateToProps
函数作为第一个参数。回顾一下:
使用订阅商店更改的Provider
组件包装根组件
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
现在<App />
包裹着connect()
的所有孩子都会成为&#34;智能&#34;零件。您可以传入mapStateToProps
来挑选州的某些部分并将其作为道具。
const mapStateToProps = (state) => {
return {
somethingFromStore: state.somethingFromStore
}
}
class ChildOfApp extends Component {
render() {
return <div>{this.props.somethingFromStore}</div>
}
}
//wrap App in connect and pass in mapStateToProps
export default connect(mapStateToProps)(ChildOfApp)
显然<App />
可以有很多孩子,你可以选择mapStateToProps
应该为每个孩子听哪个州的哪个部分。我建议您阅读usage with React上的文档,以便更好地了解这一流程。
答案 1 :(得分:10)
Redux仅提供一种通用的方式来了解商店的更新时间:subscribe
方法。对subscribe
的回调没有得到任何可能已更改的信息,因为subscribe
API故意是低级别的,并且只是运行每个没有参数的回调。所有你知道的是商店已经以某种方式更新。
因此,有人必须编写特定的逻辑来比较旧状态与新状态,并查看是否有任何变化。您可以使用React-Redux处理此问题,为组件指定mapStateToProps
函数,在组件中实现componentWillReceiveProps
,并检查商店中的特定道具是否已更改。
还有一些试图处理此案例的插件库:https://github.com/ashaffer/redux-subscribe和https://github.com/jprichardson/redux-watch。两者基本上都允许您使用不同的方法指定要查看的状态的特定部分。
答案 2 :(得分:2)
除了Andy Noelker所说的,mapStateToProps
不仅会将部分状态正确地传递到组件树中,还会订阅直接在州的这些订阅部分中进行的更改。
确实,每次更改状态的任何部分时,都会调用绑定到商店的每个mapStateToProp
函数,但调用的结果与前一个调用相比变浅 - 如果顶级键是你的话订阅没有改变(引用保持不变)。然后mapStateToProps不会调用重新渲染。因此,如果你想让这个概念起作用,你必须保持mapStateToProps简单,没有合并,类型改变或任何东西,它们应该简单地传递部分状态。
如果要在订阅时减少状态中的数据,例如,您在状态中有列表数据,并且您希望将其转换为具有ID作为键的对象,或者您希望将多个状态连接到数据结构中,则应该通过在选择器内执行所有这些修改,将mapStateToProps与createSelector
库中的reselect
结合起来。选择器是纯函数,可以减少和缓存作为输入传入的状态块,如果输入没有改变 - 它们返回与上次调用时完全相同的引用 - 而不执行缩减。
答案 3 :(得分:0)
在骇客上创建以帮助理解订户可以根据商店数据进行区分,并具有多个商店功能。
//import { createStore } from 'redux';
let createStore = require('redux').createStore;
let combineReducers = require('redux').combineReducers;
/**
* This is a reducer, a pure function with (state, action) => state signature.
* It describes how an action transforms the state into the next state.
*
* The shape of the state is up to you: it can be a primitive, an array, an object,
* or even an Immutable.js data structure. The only important part is that you should
* not mutate the state object, but return a new object if the state changes.
*
* In this example, we use a `switch` statement and strings, but you can use a helper that
* follows a different convention (such as function maps) if it makes sense for your
* project.
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
function messanger(state = 'Mr, khazi', action) {
switch(action.type) {
case 'WELCOME':
return 'Hello, Mr Khazi';
case 'BYE':
return 'Bye, Mr Khazi';
case 'INCREMENT':
return 'Incremented khazi';
default:
return state;
}
};
function latestAction(state = null, action) {
switch(action.type) {
case 'WELCOME':
return '$messanger';
case 'BYE':
return '$messanger';
case 'INCREMENT':
return '$messanger, $counter';
case 'DECREMENT':
return '$counter';
default:
return state;
}
};
let reducers = {
counts: counter,
message: messanger,
action: latestAction
};
let store = createStore(
combineReducers(reducers, latestAction)
);
// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
//let store = createStore(counter)
// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// However it can also be handy to persist the current state in the localStorage.
store.subscribe(() => {
if(store.getState().action.indexOf('messanger') !== -1) {
console.log('subscribed for counter actions', store.getState());
}
});
store.subscribe(() => {
if (store.getState().action.indexOf('counter') !== -1) {
console.log('subscribed for messanger actions', store.getState());
}
});
// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
console.log('----------------Action with both subscriber-------------');
store.dispatch({ type: 'INCREMENT' });
console.log('---------------Action with counter subscriber-----------');
store.dispatch({ type: 'DECREMENT' });
console.log('---------------Action with messenger subscriber---------');
store.dispatch({ type: 'WELCOME' });
/*
every reducer will execute on each action.
*/