一, 寫在前面
1. 前面我寫過一個vuex10分鍾入門 傳送門
2. React-redux網上有好多文檔,又臭又長,明明很簡單的問題,硬是讓新人更暈了~, 我寫這個文章的目的是讓新人在20分鍾之內明白這是怎么回事.
3. 創建你的react環境, 推薦你使用creat-react-app,我已將代碼放到gitHub,所以你可以直接clone下來,感覺不錯可以給我一個star. https://github.com/yurizhang/react-reduc-webpack
二,開始
1.我們的項目目錄:
. ├── src #開發目錄 | | | ├──action #action的文件 | | | ├──components #展示組件 | | | ├──containers #容器組件,主頁 | | | ├──reducers #reducer文件 | | | |——routes #路由文件,容器組件 | | | |——static #靜態文件 | | | ├──stores #store配置文件 | | | | | └──index.js #入口文件 | ├── build #發布目錄 ├── node_modules #包文件夾 ├── .gitignore ├── .jshintrc ├── package.json #環境配置 └── README.md #使用說明
依賴的package.json
....
"dependencies": {
"axios":
"^0.16.2",
"element-react":
"^1.0.19",
"element-theme-default":
"^1.3.7",
"react":
"^15.6.1",
"react-dom":
"^15.6.1",
"react-redux":
"^5.0.5",
"redux":
"^3.7.0"
},
三。代碼文件:
// index.js 入口文件
import React from 'react' import ReactDOM from 'react-dom' import { createStore } from 'redux' import { Provider } from 'react-redux' //import { Button,Alert } from 'element-react'; import 'element-theme-default'; import App,{SubComponent} from './containers/app.js'; //容器組件 import counter_SubComponent from './reducers/index.js'; // reducers createStore(counter),counter_SubComponent // 生成Store const store = createStore(counter_SubComponent) ReactDOM.render( <Provider store={store}> <div> <App /> <SubComponent /> </div> </Provider>, document.getElementById('root') ) /* * `store` 由 Redux 的 `createStore(reducer)` 生成 * `state` 通過 `store.getState()` 獲取,本質上一般是一個存儲着整個應用狀態的**對象** * `action` 本質上是一個包含 `type` 屬性的普通**對象**,由 Action Creator (**函數**) 產生 * 改變 `state` 必須 `dispatch` 一個 `action` * `reducer` 本質上是根據 `action.type` 來更新 `state` 並返回 `nextState` 的**函數** * `reducer` 必須返回值,否則 `nextState` 即為 `undefined` * 實際上,**`state` 就是所有 `reducer` 返回值的匯總**(本教程只有一個 `reducer`,主要是應用場景比較簡單) > Action Creator => `action` => `store.dispatch(action)` => `reducer(state, action)` => ~~`原 state`~~ `state = nextState` */
Action
/* * action cretae 這個在action\actions.js */ // Action // export const increaseAction = { type: 'increase' } // export const decreaseAction = { type: 'decrease' } // export const subTest = { type: 'test' } export const increaseAction = (text) => { return { type: 'increase', text } } export const decreaseAction = (text) => { return { type: 'decrease', text } } export const subTest = (text) => { return { type: 'test', text } } //返回一個action對象,用來關聯對應的reducer,將data保存到store。 export const saveReducer = (data) => ({ type: 'SAVE_REDUCER', data })
2個顯示組件
components\Counter.js 計數器組件
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Button } from 'element-react';
// React component
class Counter extends Component {
render() {
////從組件的props屬性中導入2個方法和一個變量
const { value, onIncreaseClick, onDecreaseClick } = this.props;
console.log('主組件里this.props:');
console.log(this.props);
return (
<div>
<span>{value}</span>
<Button type="primary" onClick={onIncreaseClick}>增加數據</Button>
<Button type="primary" onClick={onDecreaseClick}>減少數據</Button>
</div>
)
}
}
Counter.propTypes = {
value: PropTypes.number.isRequired,
onIncreaseClick: PropTypes.func.isRequired,
onDecreaseClick: PropTypes.func.isRequired
}
export default Counter
components\SubComponent.js 異步加載數據組件
import React, { Component } from 'react'
// import PropTypes from 'prop-types'
import { Alert, Button,Table } from 'element-react';
export default class SubComponent extends Component {
constructor(){
super();
this.state ={
title:''
}
}
componentWillMount() {
let id = 9999;
this.props.getTest(id) //發送get請求,然后數據 自動寫到state里
}
render() {
console.log('另一個組件里的: this.props:');
console.log(this.props);
const { test="--", testData, onTest } = this.props;
let columnName=[
{
label: "標題",
prop: "title",
width: 180
},
{
label: "年份",
prop: "year",
}
];
return (
<div>
<Alert title={test} type="info" />
<Button type="primary" onClick={onTest}>Change</Button>
<Table
style={{width: '100%'}}
columns={columnName}
maxHeight={200}
data={testData.movies}
/>
</div>
)
}
}
容器組件 container\App.js
/*容器組件 */ /* mapStateToProps, mapDispatchToProps把這個各自放到Counter和subCounter各自的組件里會不會更好? */ import { getData} from '../plugs/fetchData' import { Message } from 'element-react'; import { connect } from 'react-redux' import {increaseAction, decreaseAction, subTest, saveReducer} from '../action/actions.js'; import Counter from '../components/Counter.js'; //UI組件 import subCounter from '../components/subCounter.js'; // Map Redux state to component props function mapStateToProps(state) { console.log('主容器組件里app:state:'); console.log(state); return { value: state.counter.count, // test: state.SubComponent.test, //testData: state.SubComponent.testData } } //mapStateToProps會訂閱 Store,每當state更新的時候,就會自動執行,重新計算 UI 組件的參數,從而觸發 UI 組件的重新渲染。 // Map Redux actions to component props function mapDispatchToProps(dispatch) { return { onIncreaseClick: () => { dispatch(increaseAction()); Message('你剛做了Add的操作'); }, //調用Reducer onDecreaseClick: () => { dispatch(decreaseAction()); Message('你剛做了減少的操作'); } } } //如果mapDispatchToProps是一個函數,會得到dispatch和ownProps(容器組件的props對象)兩個參數。 //這里建議的函數,組件可以通過 this.prop讀取 // Map Redux state to component props function mapSubStateToProps(state) { console.log('子容器組件里app:state:'); console.log(state); return { //value: state.counter.count, test: state.SubComponent.test, testData: state.SubComponent.testData } } function mapSubCounterDispatchToProps(dispatch) { return { onTest: () => { dispatch(subTest()); Message('你剛做了subTest的操作'); }, //調用Reducer getTest:(id)=> { try { getData(`/facebook/react-native/master/docs/MoviesExample.json`,{id:id}).then(response=>{ //axios返回的數據是用response.data包括的,和jquery不一樣 console.log(response.data); dispatch(saveReducer(response.data)); }) // let response = await getData(`/facebook/react-native/master/docs/MoviesExample.json?id=${id}`) // await dispatch(saveReducer(response.data)) } catch (error) { console.log('error: ', error) } } } } // Connected Component export const SubComponent= connect( mapSubStateToProps, mapSubCounterDispatchToProps )(subCounter) const App= connect( mapStateToProps, mapDispatchToProps )(Counter) export default App //連接 UI組件Counter 生成一個容器組件App //connect方法接受兩個參數:mapStateToProps和mapDispatchToProps。 //它們定義了 UI 組件的業務邏輯。 //前者負責輸入邏輯,即將state映射到 UI 組件的參數(props), mapStateToProps會訂閱 Store,每當state更新的時候,就會自動執行,重新計算 UI 組件的參數,從而觸發 UI 組件的重新渲染。 //后者負責輸出邏輯,即將用戶對 UI 組件的操作映射成 Action。
recuders recuders\index.js
import { combineReducers } from 'redux'
// Action
// const increaseAction = { type: 'increase' }
// const decreaseAction = { type: 'decrease' }
// Reducer
function counter(state = { count: 0 }, action) {
const count = state.count
switch (action.type) {
case 'increase':
return { count: count + 1 }
case 'decrease':
return { count: count - 1 }
default:
return state
}
}
let initState = {
testData: [],
test: 'default'
}
function SubComponent(state = initState, action) {
switch (action.type) {
case 'test':
return { ...state, test: 'test12345' }
case 'SAVE_REDUCER':
return {
...state,
testData: action.data
}
default:
return state
}
}
//以后的業務里 這些reducers拆分成多個,這里分別導入進來
const counter_SubComponent = combineReducers({
counter,
SubComponent
})
export default counter_SubComponent;
//合並reducers讓 const store = createStore(counter_SubComponent)生成一個狀態
封裝一些插件
plugs\fetchData.js
import axios from 'axios' //BASE_URL是默認的url地址,如果你安裝了webpack,可以在webpack配置全局變量 //axios.defaults.baseURL = BASE_URL; //如果沒有安裝webpack,就使用下面這種寫法 axios.defaults.baseURL = "https://raw.githubusercontent.com/" export const getData = (url, param) => { return ( axios.get(`${url}`, {params:param}) ); } export const postData = (url, param) => { return ( axios.post(`${url}`, param) ); }
先RUN起來,后面我們來一個一個告訴這些代碼是什么意思

