今日学习
redux
react与redux结合
收获
1.redux是什么
Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理。
是一个状态管理框架,可以与包括 React 在内的许多不同的 Web 技术一起使用。
在 Redux 中,有一个状态对象负责应用程序的整个状态, 这意味着如果你有一个包含十个组件且每个组件都有自己的本地状态的 React 项目,那么这个项目的整个状态将通过 Redux?store ?被定义为单个状态对象, 这是学习 Redux 时要理解的第一个重要原则:Redux store 是应用程序状态的唯一真实来源。
这也意味着,如果应用程序想要更新状态,只能通过 Redux store 执行, 单向数据流可以更轻松地对应用程序中的状态进行监测管理。
2.redux刷题(fcc)
创建一个 Redux Store
Redux?store ?是一个保存和管理应用程序状态的state , 可以使用 Redux 对象中的?createStore() ?来创建一个 redux?store , 此方法将?reducer ?函数作为必需参数,?reducer ?函数将在后面的挑战中介绍。该函数已在代码编辑器中为你定义, 它只需将?state ?作为参数并返回一个?state ?即可。
声明一个?store ?变量并把它分配给?createStore() ?方法,然后把?reducer ?作为一个参数传入即可。
const reducer = (state = 5) => {
return state;
}
// 可从 Redux 对象获得 Redux 方法
// 例如:Redux.createStore()
// 在这里定义 store here:
var store = Redux.createStore(reducer);
从 Redux Store 获取状态
Redux store 对象提供了几种与之交互的方法, 比如,可以使用?getState() ?方法检索 Redux store 对象中保存的当前?state 。
const store = Redux.createStore(
(state = 5) => state
);
let currentState = store.getState();
定义一个 Redux Action
由于 Redux 是一个状态管理框架,因此更新状态是其核心任务之一。 在 Redux 中,所有状态更新都由 dispatch action 触发, action 只是一个 JavaScript 对象,其中包含有关已发生的 action 事件的信息。 Redux store 接收这些 action 对象,然后更新相应的状态。 有时,Redux action 也会携带一些数据。 例如,在用户登录后携带用户名, 虽然数据是可选的,但 action 必须带有?type ?属性,该属性表示此 action 的类型。
我们可以将 Redux action 视为信使,将有关应用程序中发生的事件信息提供给 Redux store, 然后 store 根据发生的 action 进行状态的更新。
var action = {
type:"LOGIN"
}
定义一个 Action Creator
创建 action 后要将 action 发送到 Redux store,以便它可以更新其状态。 在 Redux 中,可以定义动作创建器来完成此任务, action creator 只是一个返回动作的 JavaScript 函数, 换句话说,action creator 创建表示动作事件的对象。
const action = {
type: 'LOGIN'
}
// 在这里定义一个动作创建器:
function actionCreator() {
return action;
}
分发 Action Event
dispatch ?方法用于将 action 分派给 Redux store, 调用?store.dispatch() ?将从 action creator 返回的值发送回 store。
回想一下,动作创建者返回一个具有 type 属性的对象,该属性指定已发生的动作。 然后该方法会将一个 action 对象发送到 Redux store。
const store = Redux.createStore(
(state = {login: false}) => state
);
const loginAction = () => {
return {
type: 'LOGIN'
}
};
// 在这里发送 action:
store.dispatch(loginAction());
在 Store 里处理 Action
在一个 action 被创建并 dispatch 之后,Redux store 需要知道如何响应该操作。 这就是?reducer ?函数存在的意义。 Redux 中的 Reducers 负责响应 action 然后进行状态的修改。?reducer ?将?state ?和?action ?作为参数,并且它总是返回一个新的?state 。 我们要知道这是 reducer 的唯一的作用。 它不应有任何其他的作用:比如它不应调用 API 接口,也不应存在任何潜在的副作用。 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'
}
};
使用 Switch 语句处理多个 Actions
可以定义 Redux store 处理多种 action 类型。 假设在 Redux store 中管理用户身份验证。 希望用状态表示用户登录和注销。 使用 state 的?authenticated ?属性表示它。 还需要使用 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: return state;
}
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
使用 const 声明 Action Types
在使用 Redux 时的一个常见做法是将操作类型指定为只读,然后在任何使用它们的地方引用这些常量。 可以通过将 action types 使用?const ?声明重构你正在使用的代码。
const LOGIN = 'LOGIN';
const LOGOUT = '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);
const loginUser = () => {
return {
type: LOGIN
}
};
const logoutUser = () => {
return {
type: LOGOUT
}
};
注册 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);
// 全局 count 变量:
let count = 0;
store.subscribe(() => {
count++;
});
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
组合多个 Reducers
当应用程序的状态开始变得越来越复杂时,可能会将 state 分成多个块。 相反,请记住 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({
count:counterReducer,
auth:authReducer
})// 在这里定义 root reducer
const store = Redux.createStore(rootReducer);
发送 Action Data 给 Store
到目前为止,你已经学会了如何将 action dispatch 给 Redux store,但到目前为止,这些 action 并未包含除?type 之外的任何信息。 还可以和 action 一起发送特定数据。 事实上,这是非常常见的,因为 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 Thunk 中间件用于处理异步操作。
如果要使用 Redux Thunk 中间件,请将其作为参数传递给?Redux.applyMiddleware() 。 然后将此函数作为第二个可选参数提供给?createStore() ?函数, 看一下编辑器底部的代码。 然后,要创建一个异步的 action,需要在 action creator 中返回一个以?dispatch ?为参数的函数。 在这个函数中,可以 dispatch action 并执行异步请求。
在此示例中,使用?setTimeout() ?模拟异步请求。 通常在执行异步行为之前 dispatch action,以便应用程序状态知道正在请求某些数据(例如,这个状态可以显示加载图标)。 然后,一旦收到数据,就会发送另一个 action,该 action 的 data 是请求返回的数据同时也代表 API 操作完成。
请记住,正在将?dispatch ?作为参数传递给这个特殊的 action creator。 需要 dispatch action 时只需将 action 直接传递给 dispatch,中间件就可以处理其余的部分。
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) {
// 在这里发送 request action
store.dispatch(requestingData());
setTimeout(function() {
let data = {
users: ['Jeff', 'William', 'Alice']
}
// 在这里发送接收到的 data action
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 写一个计数器
const INCREMENT = 'INCREMENT'; // 为 increment action types 定义一个常量
const DECREMENT = 'DECREMENT'; // 为 decrement action types 定义一个常量
const counterReducer = (state = 0,action) => {
switch(action.type){
case INCREMENT: return state+1;
case DECREMENT: return state-1;
default: return state;
}
}; // 定义 counter reducer,根据接收到的动作递增或递减 state
const incAction = () => {
return {
type: INCREMENT
}
}; // 为自增运算定义一个动作创建器
const decAction = () => {
return {
type:DECREMENT
}
}; // 为自减运算定义一个动作创建器
const store = Redux.createStore(counterReducer); // 在这里定义 Redux store,传入 reducers
永不改变状态(在数组中使用扩展运算符)
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);
从数组中删除项目
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
switch(action.type) {
case 'REMOVE_ITEM':
let end = [...state].slice(action.index+1);
let start = [...state].slice(0,action.index);
// 这里不能修改 state,否则测试不能通过
return start.concat(end);
default:
return state;
}
};
const removeItem = (index) => {
return {
type: 'REMOVE_ITEM',
index
}
}
const store = Redux.createStore(immutableReducer);
使用 Object.assign 拷贝对象
处理对象的一个常用的方法是?Object.assign() 。?Object.assign() ?获取目标对象和源对象,并将源对象中的属性映射到目标对象。 任何匹配的属性都会被源对象中的属性覆盖。 通常用于通过传递一个空对象作为第一个参数,然后是要用复制的对象来制作对象的浅表副本。
const newObject = Object.assign({}, obj1, obj2);
这会创建?newObject ?作为新的?object ,其中包含?obj1 ?和?obj2 ?中当前存在的属性。
const defaultState = {
user: 'CamperBot',
status: 'offline',
friends: '732,982',
community: 'freeCodeCamp'
};
const immutableReducer = (state = defaultState, action) => {
switch(action.type) {
case 'ONLINE':
// 这里不能修改 state,否则测试不能通过
return Object.assign({},{
user: 'CamperBot',
status: 'online',
friends: '732,982',
community: 'freeCodeCamp'
});
default:
return state;
}
};
const wakeUp = () => {
return {
type: 'ONLINE'
}
};
const store = Redux.createStore(immutableReducer);
3.redux与react刷题(fcc)
React 和 Redux 入门
class DisplayMessages extends React.Component {
constructor(props){
super(props);
this.state = {
input:'',
messages:[]
}
}
render() {
return <div />
}
};
首先在本地管理状态
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState({
input: '',
messages: [...this.state.messages,this.state.input]
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input value={this.state.input} onChange={this.handleChange}/>
<button type="submit" onClick={this.submitMessage}></button>
<ul>
{this.state.messages.map(i => <li key={i+1}>{i}</li>)}
</ul>
</div>
);
}
};
提取状态逻辑给 Redux
// 定义 ADD、addMessage()、messageReducer() 并在这里存储:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
};
}
const messageReducer = (state = [],action) => {
switch(action.type){
case ADD: return state.concat(action.message);
default: return state;
}
}
const store = Redux.createStore(messageReducer);
使用 Provider 连接 Redux 和 React
在上一挑战中,创建了 Redux store 和 action,分别用于处理消息数组和添加新消息。 下一步要为 React 提供访问 Redux store 及发起更新所需的 actions。?react-redux ?包可帮助我们完成这些任务。
React Redux 提供的 API 有两个关键的功能:Provider ?和?connect 。 会在另一个挑战会介绍?connect 。?Provider 是 React Redux 包装 React 应用的 wrapper 组件, 它允许访问整个组件树中的 Redux?store ?及?dispatch (分发)方法。?Provider ?需要两个 props:Redux store 和 App 应用的子组件。?
用于 App 组件的?Provider ?可这样定义:
<Provider store={store}>
<App/>
</Provider>
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class DisplayMessages extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
const Provider = ReactRedux.Provider;
class AppWrapper extends React.Component {
// 在这一行下面渲染 Provider
render() {
return <Provider store={store}>
<DisplayMessages />
</Provider>
}
};
映射 State 到 Props
Provider 可向 React 组件提供?state ?和?dispatch ?,但必须确切地指定所需要的 state 和 actions, 以确保每个组件只能访问所需的 state。 完成这个任务,需要创建两个函数:mapStateToProps() 、mapDispatchToProps() 。
在这两个函数中,声明 state 中函数所要访问的部分及需要 dispatch 的创建 action 的函数。 完成这些,我们就可以迎接下一个挑战,学习如何使用 React Redux 的?connect ?方法来把函数连接到组件了。
创建?mapStateToProps() ?函数, 以?state ?为参数,然后返回一个对象,该对象把 state 映射到特定属性名上, 这些属性能通过?props ?访问组件。 由于此示例把 app 应用的整个状态保存在单一数组中,可把整个状态传给组件。 在返回的对象中创建?messages ?属性,并设置为?state 。
const state = [];
// 修改这行下面的代码
const mapStateToProps = (state) => {
return {
messages: state
}
}
映射 Dispatch 到 Props
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage(message) {
dispatch(addMessage(message));
}
}
}
连接 Redux 和 React
你已经写了mapStateToProps() 、mapDispatchToProps() ?两个函数,现在可以用它们来把?state ?和?dispatch ?映射到 React 组件的?props ?了。 React Redux 的?connect ?方法可以完成这个任务。 此方法有?mapStateToProps() 、mapDispatchToProps() ?两个可选参数, 它们是可选的,原因是你的组件可能仅需要访问?state ?但不需要分发任何 actions,反之亦然。
为了使用此方法,需要传入函数参数并在调用时传入组件。
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
**注意:**如果要省略?connect ?方法中的某个参数,则应当用?null ?替换这个参数。
const addMessage = (message) => {
return {
type: 'ADD',
message: message
}
};
const mapStateToProps = (state) => {
return {
messages: state
}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message));
}
}
};
class Presentational extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h3>This is a Presentational Component</h3>
}
};
const ConnectedComponent = ReactRedux.connect(mapStateToProps,mapDispatchToProps)(Presentational);
将 Redux 连接到 Messages App
知道如何使用?connect ?连接 React 和 Redux 后,我们可以在 React 组件中应用上面学到的内容。
在上一课,连接到 Redux 的组件命名为?Presentational ,这个命名不是任意的, 这样的术语通常是指未直接连接到 Redux 的 React 组件, 它们只负责执行接收 props 的函数来实现 UI 的呈现。 相比之下,容器组件用来连接到 Redux 上。 这些组件通常负责把 actions 分派给 store,且经常给子组件传入 store state 属性。
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
messages: []
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.setState((state) => {
const currentMessage = state.input;
return {
input: '',
messages: state.messages.concat(currentMessage)
};
});
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.state.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
// React-Redux:
const mapStateToProps = (state) => {
return { messages: state }
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (newMessage) => {
dispatch(addMessage(newMessage))
}
}
};
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
// 在这里定义 Container 组件:
const Container = connect(mapStateToProps,mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
constructor(props) {
super(props);
}
render() {
return <Provider store={store}>
<Container />
</Provider>;
}
};
将局部状态提取到 Redux 中
// Redux:
const ADD = 'ADD';
const addMessage = (message) => {
return {
type: ADD,
message: message
}
};
const messageReducer = (state = [], action) => {
switch (action.type) {
case ADD:
return [
...state,
action.message
];
default:
return state;
}
};
const store = Redux.createStore(messageReducer);
// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;
class Presentational extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
}
this.handleChange = this.handleChange.bind(this);
this.submitMessage = this.submitMessage.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
submitMessage() {
this.props.submitNewMessage(this.state.input);
this.setState((state) => ({
input: '',
}));
}
render() {
return (
<div>
<h2>Type in a new Message:</h2>
<input
value={this.state.input}
onChange={this.handleChange}/><br/>
<button onClick={this.submitMessage}>Submit</button>
<ul>
{this.props.messages.map( (message, idx) => {
return (
<li key={idx}>{message}</li>
)
})
}
</ul>
</div>
);
}
};
const mapStateToProps = (state) => {
return {messages: state}
};
const mapDispatchToProps = (dispatch) => {
return {
submitNewMessage: (message) => {
dispatch(addMessage(message))
}
}
};
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);
class AppWrapper extends React.Component {
render() {
return (
<Provider store={store}>
<Container/>
</Provider>
);
}
};
|