Redux
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。Redux 可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。Redux 支持很多界面库。
Redux:创建一个 Redux Store
在 Redux 中,有一个状态对象负责应用程序的整个状态,这意味着如果你有一个包含十个组件且每个组件都有自己的本地状态的 React 项目,那么这个项目的整个状态将通过 Redux store
被定义为单个状态对象,这是学习 Redux 时要理解的第一个重要原则:Redux store 是应用程序状态的唯一真实来源。
这也意味着,如果你的应用程序想要更新状态,只能通过 Redux store 执行,单向数据流可以更轻松地对应用程序中的状态进行监测管理。
const reducer = (state = 5) => {
return state;
}
const store = Redux.createStore(reducer);
Redux:从 Redux Store 获取状态
const store = Redux.createStore(
(state = 5) => state
);
let currentState = store.getState();
Redux:定义一个 Redux Action
由于 Redux 是一个状态管理框架,因此更新状态是其核心任务之一。在 Redux 中,所有状态更新都由 dispatch action 触发,action 只是一个 JavaScript 对象,其中包含有关已发生的 action 事件的信息。
Redux store 接收这些 action 对象,然后更新相应的状态。有时,Redux action 也会携带一些数据。例如,在用户登录后携带用户名,虽然数据是可选的,但 action 必须带有type属性,该属性表示此 action 的类型。
const action = {
type: 'LOGIN'
};
Redux:定义一个 Action Creator
创建 action 后要将 action 发送到 Redux store,以便它可以更新其状态。在 Redux 中,你可以定义动作创建器来完成此任务,action creator 只是一个返回动作的 JavaScript 函数,换句话说,action creator 创建表示动作事件的对象。
const action = {
type: 'LOGIN'
};
const actionCreator = () => {
return action;
};
Redux:分发 Action Event
dispatch
方法用于将 action 分派给 Redux store,调用store.dispatch()
将从 action creator 返回的值发送回 store。根据之前的挑战示例,以下内容是等效的,并且都 dispatch 类型为LOGIN
的 action:
store.dispatch(actionCreator());
store.dispatch({ type: 'LOGIN' });
// 任务
const store = Redux.createStore(
(state = {login: false}) => state
);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
store.dispatch(loginAction());
Redux:在 Store 里处理 Action
在一个 action 被创建并 dispatch 之后,Redux store 需要知道如何响应该操作。这就是reducer
函数存在的意义。Redux 中的 Reducers 负责响应 action 然后进行状态的修改。reducer
将state
和action
作为参数,并且它总是返回一个新的state
。我们要知道这是 reducer 的唯一的作用。reducer 只是一个接受状态和动作,然后返回新状态的纯函数。
Redux 的另一个关键原则是state
是只读的。换句话说,reducer
函数必须始终返回一个新的state
,并且永远不会直接修改状态。Redux 不强制改变状态,但是你需要在你的 reducer 函数的代码中强制执行它。
const defaultState = {
login: false
};
const reducer = (state = defaultState, action) => {
if(action.type === 'LOGIN'){
return {
login: true
}
}else {
return state;
}
};
const store = Redux.createStore(reducer);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
Redux:使用 Switch 语句处理多个 Actions
你可以定义 Redux store 如何处理多种 action 类型。比如你正在 Redux store 中进行用户身份验证,如果你希望用户在登录和注销时具有状态的响应,你可以使用具有authenticated
属性的单个的 state 对象。你还需要使用 action creators 创建与用户登录和用户注销相对应的 action,以及 action 对象本身。
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch(action.type){
case 'LOGIN': return {
authenticated: true
};
case 'LOGOUT': return {
authenticated: false
};
default: state
}
};
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
Redux:使用 const 声明 Action Types
在使用 Redux 时的一个常见做法是将操作类型指定为只读,然后在任何使用它们的地方引用这些常量。你可以通过将 action types 使用const
声明重构你正在使用的代码。
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const loginUser = () => {
return {
type: LOGIN
}
};
const logoutUser = () => {
return {
type: LOGOUT
}
};
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
switch (action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const store = Redux.createStore(authReducer);
Redux:注册 Store 监听器
在 Redux store
对象上访问数据的另一种方法是store.subscribe()
。这允许你将监听器函数订阅到 store,只要一个 action 被 dispatch 就会调用它们。这个方法的一个简单用途是为你的 store 订阅一个函数,它只是在每次收到一个 action 并且更新 store 时记录一条消息。
const ADD = 'ADD';
const reducer = (state = 0, action) => {
switch(action.type) {
case ADD:
return state + 1;
default:
return state;
}
};
const store = Redux.createStore(reducer);
let count = 0;
store.subscribe(() => {
count++;
});
store.dispatch({type: ADD});
console.log(count); // 1
store.dispatch({type: ADD});
console.log(count); // 2
store.dispatch({type: ADD});
console.log(count); // 3
Redux:组合多个 Reduces
请记住 Redux 的第一个原则:所有应用程序状态都保存在 store 中的一个简单的 state 对象中。因此,Redux 提供 reducer 组合作为复杂状态模型的解决方案。定义多个 reducer 来处理应用程序状态的不同部分,然后将这些 reducer 组合成一个 root reducer。然后将 root reducer 传递给 Redux createStore()
方法。
为了让我们将可以将多个 reducer 组合在一起,Redux 提供了combineReducers()
方法。该方法接受一个对象作为参数,在该参数中定义一个将键与特定 reducer 函数关联的属性。Redux 将使用你给的键值作为关联状态的名称。
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const counterReducer = (state = 0, action) => {
switch(action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
return state - 1;
default:
return state;
}
};
const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';
const authReducer = (state = {authenticated: false}, action) => {
switch(action.type) {
case LOGIN:
return {
authenticated: true
}
case LOGOUT:
return {
authenticated: false
}
default:
return state;
}
};
const rootReducer = Redux.combineReducers({
auth: authReducer,
count: counterReducer
});
const store = Redux.createStore(rootReducer);
Redux:发送 Action Data 给 Store
这些 action 除了可以发送 type
,还可以发送特定数据,因为 action 通常源于一些用户交互,并且往往会携带一些数据,Redux store 经常需要知道这些数据。
const ADD_NOTE = 'ADD_NOTE';
const notesReducer = (state = 'Initial State', action) => {
switch(action.type) {
case ADD_NOTE:
return action.text;
default:
return state;
}
};
const addNoteText = (note) => {
return {
type: ADD_NOTE,
text: note
};
};
const store = Redux.createStore(notesReducer);
console.log(store.getState());
store.dispatch(addNoteText('Hello!'));
console.log(store.getState());
Redux:使用中间件处理异步操作
Redux 中间件处理 Redux 应用程序中的异步请求,称为 Redux Thunk 中间件。如果要使用 Redux Thunk 中间件,请将其作为参数传递给Redux.applyMiddleware()
。然后将此函数作为第二个可选参数提供给createStore()
函数,然后,要创建一个异步的 action,你需要在 action creator 中返回一个以dispatch
为参数的函数。在这个函数中,你可以 dispatch action 并执行异步请求。
const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'
const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
const handleAsync = () => {
return function(dispatch) {
store.dispatch(requestingData());
setTimeout(function() {
let data = {
users: ['Jeff', 'William', 'Alice']
}
store.dispatch(receivedData(data));
}, 2500);
}
};
const defaultState = {
fetching: false,
users: []
};
const asyncDataReducer = (state = defaultState, action) => {
switch(action.type) {
case REQUESTING_DATA:
return {
fetching: true,
users: []
}
case RECEIVED_DATA:
return {
fetching: false,
users: action.users
}
default:
return state;
}
};
const store = Redux.createStore(
asyncDataReducer,
Redux.applyMiddleware(ReduxThunk.default)
);
Redux:用 Redux 写一个计数器
const INCREMENT = 'INCREMENT'; // 为增量 action 类型定义一个常量
const DECREMENT = 'DECREMENT'; // 为减量 action 类型定义一个常量
const counterReducer = (state=0, action) => {
switch(action.type){
case INCREMENT:
return state+1;
case DECREMENT:
return state-1;
default:
return state;
}
}; // 定义计数器,它将根据收到的action增加或减少状态
const incAction = () => {
return {
type: INCREMENT
}
}; // 定义一个用于递增的 action creator
const decAction = () => {
return {
type: DECREMENT
}
}; // 定义一个用于递减的 action creator
const store = Redux.createStore(counterReducer); // 定义一个 Redux store,传递你的 reducer
Redux:永不改变状态
const ADD_TO_DO = 'ADD_TO_DO';
const todos = [
'Go to the store',
'Clean the house',
'Cook dinner',
'Learn to code',
];
const immutableReducer = (state = todos, action) => {
switch(action.type) {
case ADD_TO_DO:
return [...state,action.todo];
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}
const store = Redux.createStore(immutableReducer);
Redux:在数组中使用扩展运算符
const immutableReducer = (state = ['Do not mutate state!'], action) => {
switch(action.type) {
case 'ADD_TO_DO':
return [...state, action.todo];
default:
return state;
}
};
const addToDo = (todo) => {
return {
type: 'ADD_TO_DO',
todo
}
}
const store = Redux.createStore(immutableReducer);
Redux:从数组中删除项目
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
switch(action.type) {
case 'REMOVE_ITEM':
let a = [...state].slice(action.index + 1);
let b = [...state].slice(0, action.index);
return b.concat(a);
default:
return state;
}
};
const removeItem = (index) => {
return {
type: 'REMOVE_ITEM',
index
}
}
const store = Redux.createStore(immutableReducer);
Redux:使用 Object.assign 拷贝对象
const defaultState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const immutableReducer = (state = defaultState, action) => {
switch(action.type) {
case 'ONLINE':
let a = Object.assign({},state);
a.status = 'online';
return a;
default:
return state;
}
};
const wakeUp = () => {
return {
type: 'ONLINE'
}
};
const store = Redux.createStore(immutableReducer);