fullstackReact 学习笔记 flux和redux
一、为什么使用flux?
因为react中将所有的state都保存在最顶部的组件中,然后通过props传递到下面各个组件中,下面各个组件的action将会通过回调函数层层传递到最顶层,最顶层组件的处理函数负责处理下面各个组件的action,并更新state。由于,复杂的项目一个action可能要更新很多种state,由此造成了顶部处理函数的庞大、集中,由此导致容易出错。
是否可以将state的更新分开管理,因此产生了flux.
flux其实是一种设计模式:
action=>dispatcher=>store=>viewer
就是:不在是action直接更新state进而更新viewer,而是在中间增加了一层store,store负责管理所有的state,当action需要更新state时候,将action通过dispatcher传递给store,store负责更新state,进而刷新viewer.
flux的好处:
(1)将state的管理独立出来,减轻了顶部组件的责任。
(2)react component将变大的更加的简化,由于component对应state的管理全部在外部store中进行,由此可以将react component设计为简单的render() html标签的函数。
(3)不用再匹配state tree和dom tree。如:你想在dom中显示一个人性化的timeStamp,其实state存储成一个时间点是最好的,但是如果state在react component中管理的话,就必须匹配state到dom中,这其中就必然将转换函数包含在组件中,以保持dom和state的一致性。
二、redux:flux的一种好的实践方法
redux其实很简单,核心代码不到100行。redux的核心观念:
(1)所有的state全部在store中存储,应用需要state时,需要通过store.getState()得到当前的全局的state.
(2)state在store外部是不会发生变动的。views需要将修改state的action发送到store中dispatch(action)=》store,然后store调用reducer以生成新的全局的state.
在store内部:
newstate=reducer(oldstate,action)
a、action来自view传递(dispatch)的一个对象{type:' '},必须包含一个type动作,便于在reducer中分类处理。
b、reducer接受上一次的state和viewer传递的action生成一个新的state
oldstate是上一次的state,如果没有处理过则为初始化的state.
newState生成后通过回调用所有的监听函数,让所有监听的组件(subscribe)进行更新。
示例:counter
aciton:
const incrementAction = { type: 'INCREMENT' };
const decrementAction = { type: 'DECREMENT' };
const unknownAction = { type: 'UNKNOWN' };
action中还可以附带其他的参数,如制定每次增减的数量
const incrementAction = {
type: 'INCREMENT',
amount: 5,
};
reducer:
function reducer(state, action) {
if (action.type === 'INCREMENT') {
return state + 1;
} else if (action.type === 'DECREMENT') {
return state - 1;
} else {
return state;
}
}
reducer中处理action中附带的参数:
function reducer(state, action) {
if (action.type === 'INCREMENT') {
return state + action.amount;
} else if (action.type === 'DECREMENT') {
return state - action.amount;
} else {
return state;
}
}
三、模拟store
store负责维护state,接受action,并调用reducer生成新的state.
它至少要提供两个功能:
getState()获取state.
dispatch()接受action
我们通过createStore来创建store
示例:
function createStore(reducer) {
let state = 0;
const getState = () => (state);
const dispatch = (action) => {
state = reducer(state, action);
};
return {
getState,
dispatch,
};
}
createStore接受reducer,返回两个功能getState和dispatch
使用createStore重写counter:
const store = createStore(reducer);
const incrementAction = {
type: 'INCREMENT',
amount: 3,
};
store.dispatch(incrementAction);
console.log(store.getState());
dispatch仅仅发送动作给reducer,他不负责返回处理结果,处理后的state将通过getState接口返回。
工厂函数:
因为state在cerateStore中声明的私有变量,要保持state只有一份,以便于所有的调用都更新一个state,因此,store只能有一个,只能调用一次createStore
注意:reducer必须是纯函数
四、示例:构造一个chat app
1、state
const initialState = { messages: [] };
state是包含一组信息的对象
2、actions
增加信息:
const addMessageAction1 = {
type: 'ADD_MESSAGE',
message: 'How does it look, Neil?',
};
删除信息:
const deleteMessageAction = {
type: 'DELETE_MESSAGE',
index: 0,
};
3、reducer
function reducer(state, action) {
if (action.type === 'ADD_MESSAGE') {
return {
messages: state.messages.concat(action.message),
};
} else if (action.type === 'DELETE_MESSAGE') {
return {
messages: [
...state.messages.slice(0, action.index),
...state.messages.slice(
action.index + 1, state.messages.length
),
],
};
} else {
return state;
}
}
4、createStore,
接受一个初始化的state
function createStore(reducer, initialState) {
let state = initialState;
const getState = () => (state);
const dispatch = (action) => {
state = reducer(state, action);
};
return {
getState,
dispatch,
};
}
5、store
const store = createStore(reducer, initialState);
6、使用store提供的功能dispatch和getState
store.dispatch(addMessageAction1); const stateV1 = store.getState();
7、component订阅更新
由于我们在react component 之外更新了state,因此无法像在react component 内部一样使用setState直接更新。因此我们这里使用观察着模式来更新组件。
观察者模式:
(1)在store内部创建一个观察者列表:listeners:[]
(2)对于需要更新state的component,可以调用一个subscribe()将listener添加到store中的listeners.
(3)每次完成新的state的计算,即newState=reducer(oldstate,action)之后,遍历观察者列表中的listener,并执行。这样所有的观察则组件便完成了更新
store内部实现代码:
function createStore(reducer, initialState) {
let state = initialState;
const listeners = [];
// ...
const subscribe = (listener) => (
listeners.push(listener)
);
const getState = () => (state);
// ...
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach(l => l());
};
// ...
// ...
return {
subscribe,
getState,
dispatch,
};
}
使用listener
const store = createStore(reducer, initialState);
const listener = () => {
console.log('Current state: ');
console.log(store.getState());
};
store.subscribe(listener);
const addMessageAction1 = {
type: 'ADD_MESSAGE',
message: 'How do you read?',
};
store.dispatch(addMessageAction1);
这样每次传递完action之后,便完成了自动的控制台输出。
8、将react 和redux联系起来
(1)使用store.getState()获取state,而不是在最顶层的component中管理state
(2)使用store.subscribe()订阅更新,(使用生命周期函数componentDidMount()中强制更新(forceUpdate)),而不再使用setState去手动更新。
(3)使用store.dispatch()传递action,而不再需要通过回调函数,回调最顶层组件的处理函数处理数据了。
class App extends React.Component {
componentDidMount() {
store.subscribe(() => this.forceUpdate());
}
render() {
const messages = store.getState().messages;
return (
<div className='ui segment'>
<MessageView messages={messages} />
<MessageInput />
</div>
);
}
}
class MessageInput extends React.Component {
state = {
value: '',
};
onChange = (e) => {
this.setState({
value: e.target.value,
})
};
handleSubmit = () => {
store.dispatch({
type: 'ADD_MESSAGE',
message: this.state.value,
});
this.setState({
value: '',
});
};
render() {
return (
<div className='ui input'>
<input
onChange={this.onChange}
value={this.state.value}
type='text'
/>
<button
onClick={this.handleSubmit}
className='ui primary button'
type='submit'
>
Submit
</button>
</div>
);
}
}
class MessageView extends React.Component {
handleClick = (index) => {
store.dispatch({
type: 'DELETE_MESSAGE',
index: index,
});
};
render() {
const messages = this.props.messages.map((message, index) => (
<div
className='comment'
key={index}
onClick={() => this.handleClick(index)}
>
{message}
</div>
));
return (
<div className='ui comments'>
{messages}
</div>
);
}
}
注意:我们在存在input的组件中保留了state,这是一个通用的实践,便于更新state.
浙公网安备 33010602011771号