25.redux回顧,redux中的action函數異步

回顧:Redux: 相似於 Vuex

概念:store/reducer/action

action:動做 {type,.....} 必定要有type 其餘屬性不作限制css

reducer:經過計算產生state
公式:(state,action)=>newStatehtml

store: 容器vue

getState() 獲取全部狀態react

dispatch(action) dispatch裏面能夠跟對象和函數, —— 函數須要單獨處理——中間件
subscribe(監聽函數);—— watchajax

觸發條件:
一、dispatch ---> reducer
二、必須產生一個新的狀態 newStatevuex

exp1:npm

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,connect}

Provider:提供 做用: 把狀態 store共享給全部的子組件 包在 json

connect 用法: connect()(Comp); ---> this.props --> {dispatch}redux

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

exp1:api

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==
第一步:

一、安裝 cnpm i -S redux-thunk
二、引入 import thunk from "redux-thunk";
三、redux中引入applyMiddleware
import {createStore,applyMiddleware} from "redux";
四、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

相關文章
相關標籤/搜索