25.redux回顾,redux中的action函数异步

回顾:Redux: 类似于 Vuex

概念:store/reducer/action

action:动作 {type,.....} 一定要有type 其他属性不做限制

reducer:通过计算产生state
公式:(state,action)=>newState

store: 容器

getState() 获取所有状态

dispatch(action) dispatch里面可以跟对象和函数, —— 函数需要单独处理——中间件
subscribe(监听函数);—— watch

触发条件:
1、dispatch ---> reducer
2、必须产生一个新的状态 newState

exp1:

1.ction.js
export const PLUS = Symbol("PLUS");
export const MINUS = Symbol("MINUS");

export function plusAction(){
    return {type:PLUS};
}
export function minusAction(){
    return {type:MINUS};
}

2.reducer.js

import {PLUS} from "./actions"
//数据初始化
const initState = {count:1};

//创建reducer
const reducer = (state = initState,action = {})=>{
    const {type} = action;
    switch(type){
        case PLUS:
            return {...state,count:state.count+1};
        default:
            return state;
    }
}
export default reducer;

3.store

//引入
import {createStore} from "redux";
import reducer from "./reducer";
//创建store
const store = createStore(reducer);
store.subscribe(()=>console.log(store.getState()));
export default store;

4.App.js

import React, { Component } from 'react';
class App extends Component {
  render() {
    return (
      <div className="App">
        App
      </div>
    );
  }
}
export default App;

5.Counter.js

//引入
import React,{Component} from "react";
import {bindActionCreators} from "redux";
import {connect} from "react-redux";
import {plusAction,minusAction} from "./actions";

//创建组件
class Counter extends Component{
    render(){
        console.log(11111,this.props);
        const {count,plusAction} = this.props;

        return (
            <div>
                {count}
                <input onClick={plusAction} value="+" type="button"/>
            </div>
        );
    }
}

const mapStateToProps = state =>({
    count:state.count
});

function mapDispatchToProps(dispatch){
    return bindActionCreators({plusAction,minusAction},dispatch);
}

export default connect(mapStateToProps,mapDispatchToProps)(Counter);

react-redux:

Provider:提供 作用: 把状态 store共享给所有的子组件 包在

connect 用法: connect()(Comp); ---> this.props -->

connect(mapStateToProps,xxxxxxx)(Comp); ---> this.props -->{ state, dispatch }

exp1:

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from "react-redux";

import store from "./Counter/store";
import App from "./Counter/App";
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.getElementById('root'));

registerServiceWorker();

image

异步action —— 在原有的异步函数内 包个函数—— 函数会有一个dispatch参数

在action里面有延迟操作!定时器、数据交互(ajax,fetch...)

用redux-thunk
第一步:

1、安装 cnpm i -S redux-thunk
2、引入 import thunk from "redux-thunk";
3、redux中引入applyMiddleware
import {createStore,applyMiddleware} from "redux";
4、const store = createStore(reducer,applyMiddleware(thunk));

第二步:

++在异步anctions中使用++
问题1:

++定时器++

function plusAsyncAction(){
	setTimeout(()=>{  
			return {type:"PLUS"};
	},1000);
}

通过中间件来解决

function plusAsyncAction(){
	return function(dispatch){
		setTimeout(()=>{
			  //自己手动派发action
				dispatch({type:"PLUS"});
		},1000);
	}
}

exp1:
import {createStore,applyMiddleware} from "redux";引入模块
const store = createStore(reducer,applyMiddleware(thunk)); 使用模块

import React,{Component} from "react";
import {createStore,applyMiddleware} from "redux";
import thunk from "redux-thunk";

//创建store
//数据初始化
const initState = {count:1};

//创建reducer
const reducer = (state=initState,action={})=>{
    const {type} = action;
    switch(type){
        case "PLUS":
            return {...state,count:state.count+1};
        default:
            return state;
    }
}

//创建store
const store = createStore(reducer,applyMiddleware(thunk));

function plusAction(){
    return {type:"PLUS"};
}

function plusAsyncAction(){
    return function(dispatch,getState){
        const val = getState();//获取是修改之前的状态
        console.log(val,getState);
        setTimeout(()=>{
            //dispatch({type:"PLUS"});
            dispatch(plusAction());
        },1000)
    }
}

class Counter extends Component{

    constructor(...args){
        super(...args);
        store.subscribe(()=>{
            console.log(store.getState());
            this.forceUpdate();
        });
    }

    plus(){
        store.dispatch(plusAsyncAction());
    }
    render(){
        return (
            <div>
                {store.getState().count}
                <input onClick={this.plus.bind(this)} value="+" type="button"/>
            </div>
        );
    }
}
export default Counter;

res:
延迟一秒显示
image


问题2:

++交互(数据交互(ajax,fetch...))++

function fetchAsyncAction(){
		
		fetch(url).then.then(data=>{
			
			return {type:"GET",payload:data};
		
		})
		
		//return undefined
}

通过中间件来解决

function fetchAsyncAction(){
		
		return function(dispatch){
			fetch(url).then.then(data=>{
				dispatch({type:"GET",payload:data});
			})
		}
}

exp2:
数据交互

import React,{Component} from "react";
import {createStore,applyMiddleware} from "redux";
import thunk from "redux-thunk";
const url = "http://localhost:9000/api/v2/movie/in_theaters?city=北京";
//创建store
//数据初始化
const initState = {subjects:[]};
//创建reducer
const reducer = (state = initState,action = {})=>{
    const {type} = action;
    switch(type){
        case "GET":
            return {...state,subjects:action.payload};
        default:
            return state;
    }
}
//创建store
const store = createStore(reducer,applyMiddleware(thunk));

function fetchSyncAction(){
    return function(dispatch){
        fetch(url).then(res=>res.json()).then(data=>{
            console.log(data.subjects);
            dispatch({type:"GET",payload:data.subjects});
        })
    }
}

//创建组件
class Fetch extends Component{
    constructor(...args){
        super(...args);
        store.subscribe(()=>{
            console.log(store.getState());
            this.forceUpdate();
        })
    }
    asyncFecth(){
        store.dispatch(fetchSyncAction());
    }
    fn(){

        //url:"http://api.douban.com/v2/movie/in_theaters?city=北京",
        //url:"http://localhost:9000/api/v2/movie/in_theaters?city=北京",
        
        //store.dispatch(plusAsyncAction());
        fetch(url).then(res=>res.json()).then(data=>{
            console.log(data.subjects);

            store.dispatch({type:"GET",payload:data.subjects});
        })
    }

    render(){
        return (
            <div>
                {
                   store.getState().subjects.map(({id,title})=>{
                    return <div key={id}>{title}</div>;
                    })
                }
                <input onClick={this.fn.bind(this)} value="普通fetch" type="button"/>
                <input onClick={this.asyncFecth.bind(this)} value="asyncFecth" type="button"/>
            </div>
        );
    }

}

export default Fetch;

res:
image

        dispatch(action)                 
View  ------------------->  reducer ---------> newState 


            异步                                同步
       dispatch(asyncAction)                 dispatch(action)
View  ----------------------> middleware拦截 --------------->  reducer --------> newState

属性验证:

安装:cnpm i -D prop-types

参考:https://reactjs.org/docs/typechecking-with-proptypes.html?#___gatsby

Test.propTypes = {
		属性:验证规则
		optionalArray: PropTypes.array,
	  optionalBool: PropTypes.bool,
	  optionalFunc: PropTypes.func,
	  optionalNumber: PropTypes.number,
	  optionalObject: PropTypes.object,
	  optionalString: PropTypes.string,
	  optionalSymbol: PropTypes.symbol,

		
}

exp1:

import React, { Component } from "react";
import PropTypes from "prop-types";

class Test extends Component {
    //判断name是否是字符串,若不是字符串则报错
    //,isRequired表示空也判断
    //写法二
    static propTypes = {
        name: PropTypes.string.isRequired,
    }
    
  render() {
      const {name} = this.props;
    return (
      <div>属性验证:name: {name}</div>
    );
  }
}
//写法一
/*Test.propTypes = {
    name: PropTypes.string.isRequired,
}*/

export default Test;

res:
符合判定不返回任何值,不符合则会报错,并提醒输入的是何种类型数据.

image

exp2:
自定义属性验证

import React, { Component } from "react";
// import PropTypes from "prop-types";

function Test(props){
    return (
      <div>属性验证:name: {props.name}</div>
    );
}

const PropTypes = {
    string:function(props, propName, componentName){
        if(typeof props[propName] !== "string"){
            return new Error(`你输入的${propName}我期望的是 字符串 ,但是你给我的是 ${typeof props[propName]} `);
        }

    }
}

Test.propTypes = {
    name:PropTypes.string,
}
export default Test;

res:
image

在异步anctions中使用的案例
豆瓣网:

image

代码:

1.App.js

import React, { Component } from "react";
import { Provider} from "react-redux";
import store from "./store";
import MoveBox from "./MoveBox";
import "./index.css";
let arr = [
  {id:1,city:"北京"},
  {id:2,city:"上海"},
  {id:3,city:"深圳"},
  {id:4,city:"青岛"}
];

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <div className="App">
          <MoveBox arr={arr}/>
        </div>
      </Provider>
    );
  }
}
export default App;
-----------------------------------
2.index.css

.active{background:pink;}
-----------------------------------
3.reducer.js

import {QUERY} from "./actions"

const initState = {
    id:1,
    city:"北京"
};

export default (state = initState,action = {})=>{
    switch(action.type){
        case QUERY:
            return {...state,id:action.payload.id,city:action.payload.city};
        default:
            return state;
    }

};
-------------------------------

4.store.js
import {createStore, applyMiddleware} from "redux";
import thunk from "redux-thunk";
import reducer from "./reducer";
const store = createStore(reducer,applyMiddleware(thunk));

//测试用
store.subscribe(()=>{
    console.log("store:",store.getState())
});

export default store;
----------------------------------
5.action.js

export const QUERY = Symbol("QUERY");

------------------------------------
6.MoveBox.js
import React, { Component } from "react";
import { connect } from "react-redux";
import MoveTitle from "./MoveTitle";
import MoveList from "./MoveList";

//url:"http://api.douban.com/v2/movie/in_theaters?city=北京",
//url:"http://localhost:9000/api/v2/movie/in_theaters?city=北京",
class MoveBox extends Component {
  render() {
      console.log("this.props:",this.props);
      const {id,city,arr} = this.props;
    return (
      <div>
        <MoveTitle id={id} arr={arr}/>
        <MoveList city={city} />
      </div>
    );
  }
}

function mapStateToProps(state){
    return {id:state.id,city:state.city};
}

export default connect(mapStateToProps)(MoveBox);
-------------------------------------------
7.MoveTitle.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { QUERY } from "./actions";
class MoveTitle extends Component {


    fn(item){
        this.props.dispatch({type:QUERY,payload:item});
    }

  render() {
      const {id,arr} = this.props; 
    return (
      <ul> 
          {
              arr.map(item=>{
                  return <input onClick={this.fn.bind(this,item)} className={item.id === id?"active":""} key={item.id} type="button" value={item.city}/>
              })
          }
          
      </ul>
    );
  }
}

export default connect()(MoveTitle);

--------------------------------------
8.MoveList.js
import React, { Component } from "react";

//http://localhost:9000/api/v2/movie/in_theaters?city=北京",

const url = "http://localhost:9000/api/v2/movie/in_theaters?city=";

class MoveList extends Component {

    state = {title:this.props.city,subjects:[]};

    UNSAFE_componentWillReceiveProps(props){

        console.log("UNSAFE_componentWillReceiveProps",props,this.props);
        this.featcData(props.city);
    }
    componentDidMount(){
        this.featcData();
    }

    featcData(city){
        city = city||this.props.city
        fetch(`${url}${city}`).then(res=>res.json()).then(data=>{
            console.log(111,data);
            this.setState(data);
        });
    }
  render() {
    return (
      <div> 
        {this.state.title}
        <hr />
        <ul>
            {
                this.state.subjects.map(item=>{
                    return <li key={item.id}>{item.title}</li>
                })
            }
        </ul>


      </div>
    );
  }
}

export default MoveList;

res:
image

posted @ 2018-08-14 21:27  飞刀还问情  阅读(1753)  评论(0编辑  收藏  举报