1_開發環境搭建
1.1創建項目並編寫基礎代碼
安裝node.js、腳手架create-react-app等步驟詳見:Easy的React學習筆記(一.基礎),不再贅述。
在命令提示符(管理員)中,用腳手架create-react-app創建項目:
D:\Easy's code\ReduxDemo>create-react-app demo01
創建好惡demo01目錄如下,把src目錄內文件全刪了,重新創建index.js、TodoList.js。
分別修改index.js、TodoList.js的代碼。
index.js:
import React from 'react';
import ReactDOM from 'react-dom'
import TodoList from './TodoList'
ReactDOM.render(<TodoList />,document.getElementById('root'))
TodoList.js(可以用rcc快速生成,前提是安裝了React快速生成代碼插件Simple React Snippets):
import React, { Component } from 'react';
class TodoList extends Component {
render() {
return (
<div>
</div>
);
}
}
export default TodoList;
注:React快速生成代碼插件Simple React Snippets的安裝與使用
1.2安裝Ant Design
網絡慢的建議用cnpm
cnpm install antd --save
報錯:
peerDependencies WARNING [email protected] › rc-picker@~1.4.16 requires a peer of dayjs@^1.8.18 but none was installed
其實是缺少依賴的問題(這類缺少依賴的問題的詳細解決方法見:https://blog.csdn.net/qq_37279880/article/details/106180838)
加上:
cnpm install dayjs@^1.8.18 --save
爲何要加--save這樣安裝?
因爲--save這種寫法可以添加依賴,詳見博文: npm install的4種常用方式的區別(附表格對比)
再試次一:
cnpm install antd --save
安裝成功:
2_用AntDesign製作UI界面
TodoList.js引入AntDesign的css樣式:
import 'antd/dist/antd.css'
引入要用的組件:
import {Input,Button,List} from 'antd'
引入要用的icon:
import { UserOutlined } from '@ant-design/icons';
用vscode的話可以安裝auto import插件,在引入過一次這個組件之後,第二次在另一個個文件引入就會有自動引入提示了:
vscode 自動導入包(組件)插件Auto Import安裝好後,依舊沒有自導導入提示的原因
編寫input框、button按鈕:
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { UserOutlined } from '@ant-design/icons';
import {Input,Button,List} from 'antd'
class TodoList extends Component {
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input placeholder='xxx' style={{width:'233px', marginRight:'10px'}} prefix={<UserOutlined />} />
<Button type="primary">增加</Button>
</div>
</div>
);
}
}
export default TodoList;
加入List列表後,完整代碼如下:
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { UserOutlined } from '@ant-design/icons';
import {Input,Button,List} from 'antd'
const data = [
'寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb',
'奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb',
'奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb'
]
class TodoList extends Component {
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input placeholder='xxx' style={{width:'233px', marginRight:'10px'}} prefix={<UserOutlined />} />
<Button type="primary">增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
dataSource={data}
renderItem={item=>(<List.Item>{item}</List.Item>)}
/>
</div>
</div>
);
}
}
export default TodoList;
3_創建Redux中的倉庫:Store、Reducers
Redux Flow圖:
Redux工作流程中有四個部分,最重要的就是store這個部分,因爲它把所有的數據都放到了store中進行管理。
在項目目錄安裝Redux:
npm install --save redux
安裝好redux之後,在src目錄下創建一個store並在文件夾下創建index.js,index.js就是整個項目的store文件,
store/index.js:
import {createStore} from 'redux' // 引入createStore方法
const store = reactStore() // 創建數據存儲倉庫
export default store //暴露出去
這樣雖然已經建立好了倉庫,但是這個倉庫很混亂,這時候就需要一個有管理能力的模塊出現,這就是Reducers。這兩個一定要一起
創建出來,這樣倉庫纔不會出現互懟現象。在store文件夾下,新建一個文件reducer.js。
store/reducer.js:
const defaultState = {} //默認數據
export default (state = defaultState,action)=>{ //就是一個方法函數
return state
}
state: 是整個項目中需要管理的數據信息,這裏我們沒有什麼數據,所以用空對象來表示。
把reducer引入到store中,創建store時,reduce以參數的形式傳遞給store,
修改store/index.js:
import {createStore} from 'redux' // 引入createStore方法
import reducer from './reducer'
const store = reactStore(reducer) // 創建數據存儲倉庫
export default store //暴露出去
倉庫store和reducer都創建好了,可以初始化一下todoList中的數據了。在reducer.js文件的defaultState對象中,加入兩個屬
性:inputValue和list,這就相當於你給Store裏增加了兩個新的數據。
store/reducer.js:
const defaultState = {
inputValue:'寫點東西',
list:[
'寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb',
'奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb',
'奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb'
]
} //默認數據
export default (state = defaultState)=>{ //就是一個方法函數
return state
}
TodoList.js引入store中的數據:
import store from './store/index'
引入store後可以試着在構造方法裏打印到控制檯一下,看看是否真正獲得了數據,如果正常,是可以輸出store中的數據的:
constructor(props) {
super(props);
console.log(store.getState())
}
正常輸出store中數據:
測試數據正常後,直接讓this.state=store.getState(),把store的數據給TodoList,就可以把Input組件的placeholder、List的dataSource換成store的數據了。
修改TodoList.js:
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { UserOutlined } from '@ant-design/icons';
import {Input,Button,List} from 'antd'
import store from './store/index'
class TodoList extends Component {
constructor(props) {
super(props);
this.state=store.getState()
// console.log(store.getState())
}
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input placeholder={this.state.inputValue} style={{width:'233px', marginRight:'10px'}} prefix={<UserOutlined />} />
<Button type="primary">增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
dataSource={this.state.list}
renderItem={item=>(<List.Item>{item}</List.Item>)}
/>
</div>
</div>
);
}
}
export default TodoList;
通過上面的步驟,實現了創建store、reduce和如何使用store中的數據。
4_Redux devtools插件的安裝與配置
4.1在chrome網上應用店搜索並安裝。(不翻牆上chrome網上應用店的方法)
4.2配置Redux devtools
直接在Redux devtools的github上覆制下面那段話到store/index.js中:
import {createStore} from 'redux' // 引入createStore方法
import reducer from './reducer' //引入管理模塊reducer
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
) // 創建數據存儲倉庫
export default store //暴露出去
配置好的效果:
5_通過Input組件體驗Redux流程
我們要實現的是在TodoList.js中文本框的值改變的話,redux
中store
的值就跟着改變,並且如果Redux
中的state
值改變,組件也跟着改變。即如下圖的流程:
5.1爲Input組件綁定onChange事件
如果想Input改變,redux也跟着改變,需要在Input組件上增加onChange響應事件(TodoList.js):
<Input
placeholder={this.state.inputValue}
style={{width:'233px', marginRight:'10px'}}
prefix={<UserOutlined />}
onChange={this.changeInputValue}
/>
構造函數綁定onChange事件的this指向(TodoList.js):
constructor(props) {
super(props);
this.state=store.getState()
// console.log(store.getState())
this.changeInputValue=this.changeInputValue.bind(this)
}
render下方寫changeInputValue方法(TodoList.js):
changeInputValue(e){
console.log(e.target.value)
}
注:onchange 事件會在域的內容改變時發生。
onChange綁定成功:
TodoList.js(完整代碼):
import React, { Component } from 'react';
import 'antd/dist/antd.css'
import { UserOutlined } from '@ant-design/icons';
import {Input,Button,List} from 'antd'
import store from './store/index'
class TodoList extends Component {
constructor(props) {
super(props);
this.state=store.getState()
// console.log(store.getState())
this.changeInputValue=this.changeInputValue.bind(this)
}
render() {
return (
<div style={{margin:'10px'}}>
<div>
<Input
placeholder={this.state.inputValue}
style={{width:'233px', marginRight:'10px'}}
prefix={<UserOutlined />}
onChange={this.changeInputValue}
/>
<Button type="primary">增加</Button>
</div>
<div style={{margin:'10px',width:'300px'}}>
<List
bordered
dataSource={this.state.list}
renderItem={item=>(<List.Item>{item}</List.Item>)}
/>
</div>
</div>
);
}
changeInputValue(e){
// console.log(e.target.value)
const action = {
type:'change_input_value',
value:e.target.value
}
store.dispatch(action)
}
}
export default TodoList;
5.2創建Action
想改變Redux的State值,就要創建Action。
Action就是一個對象,這個對象一般有兩個屬性,第一個是對Action的描述,第二個是要改變的值。
action創建之後,通過dispatch()方法傳遞給store。
修改changeInputValue方法(TodoList.js):
changeInputValue(e){
// console.log(e.target.value)
const action = {
type:'changeInput',
value:e.target.value
}
store.dispatch(action)
}
這是Action就已經完全創建完成了,也和store有了聯繫。
注;store.dispatch:分發 action。這是觸發 state 變化的惟一途徑。
5.3store自動推送策略
store只是一個倉庫,它並沒有管理能力,它會把接收到的action自動轉發給Reducer。直接在Reducer中打印出結果看一下。
修改store/reducer.js:
const defaultState = {
inputValue:'寫點東西',
list:[
'寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb寶馬nb',
'奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb奧迪nb',
'奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb奔馳nb'
]
} //默認數據
export default (state = defaultState,action)=>{ //就是一個方法函數
console.log(state,action)
return state
}
state: 原始倉庫裏的狀態。
action: action新傳遞的狀態。
通過打印知道,Reducer已經拿到了原來的數據state和新傳遞過來的數據action,現在要改變store的值。我們先判斷type是不是正確的,如果正確,我們需要重新聲明一個變量newState。(注意:Reducer裏只能接收state,不能改變state。),所以我們聲明瞭一個新變量,然後再用return返回回去。
export default (state = defaultState,action)=>{ //就是一個方法函數
console.log(state,action)
if(action.type==='changeInput'){
let newState =JSON.parse(JSON.stringify(state))////深度拷貝state
newState.inputValue = action.value
return newState
}
return state
}
注:
JavaScript JSON.parse():用於將一個 JSON 字符串轉換爲對象
JSON.stringify():將 JavaScript 對象轉換爲字符串
let cloneObj=JSON.parse(JSON.stringify(obj)):深拷貝最簡單的固定寫法