一, 写在前面
1. 前面我写过一个vuex10分钟入门
2. React-redux网上有好多文档,又臭又长,明明很简单的问题,硬是让新人更晕了~, 我写这个文章的目的是让新人在20分钟之内明白这是怎么回事.
3. 创建你的react环境, 推荐你使用creat-react-app,我已将代码放到,所以你可以直接clone下来,感觉不错可以给我一个star.
二,开始
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// 生成Storeconst store = createStore(counter_SubComponent)ReactDOM.render(, 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 componentclass Counter extends Component { render() { ////从组件的props属性中导入2个方法和一个变量 const { value, onIncreaseClick, onDecreaseClick } = this.props; console.log('主组件里this.props:'); console.log(this.props); return ({value}) }}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 () }}
容器组件 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 propsfunction 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 propsfunction mapDispatchToProps(dispatch) { return { onIncreaseClick: () => { dispatch(increaseAction()); Message('你刚做了Add的操作'); }, //调用Reducer onDecreaseClick: () => { dispatch(decreaseAction()); Message('你刚做了减少的操作'); } }}//如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。//这里建议的函数,组件可以通过 this.prop读取// Map Redux state to component propsfunction 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 Componentexport 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起来,后面我们来一个一个告诉这些代码是什么意思