2019年最新 React从入门到实战(带 React 企业级实战项目-宜居)

(React-Redux基础知识&)

React-Redux基础知识&

41.Redux 回顾组件传递及Redux介绍

(Redux 回顾组件传递及Redux介绍&)

Redux 回顾组件传递及Redux介绍&

React从入门到实战- 企业级实战项目-宜居二_App

React从入门到实战- 企业级实战项目-宜居二_App_02

父传子

React从入门到实战- 企业级实战项目-宜居二_react_03

React从入门到实战- 企业级实战项目-宜居二_App_04

React从入门到实战- 企业级实战项目-宜居二_数据_05

子传父

React从入门到实战- 企业级实战项目-宜居二_react_06

接收数据

React从入门到实战- 企业级实战项目-宜居二_ico_07

默认什么都没有点击传值

React从入门到实战- 企业级实战项目-宜居二_App_08

React从入门到实战- 企业级实战项目-宜居二_App_09

React从入门到实战- 企业级实战项目-宜居二_ico_10

普通情况只能组件间传递数据,redux子组件把数据传递给仓库,store负责把数据分发各个组件

React从入门到实战- 企业级实战项目-宜居二_数据_11

把数据提交到公共的stroe仓库中

React从入门到实战- 企业级实战项目-宜居二_数据_12

组件间通信

src\components\coms\Parent.jsx

import React from "react"
import Child from "./Child"

export default class Parent extends React.Component{

    state = {
        value:""
    }

    clickHandle = (data) =>{
        this.setState({
            value:data
        })
    }

    render(){
        return(
            <div>
                Parent:{ this.state.value }
                <Child title="子标题" onMyEvent={ this.clickHandle }/>
            </div>
        )
    }
}

src\components\coms\Child.jsx

import React from "react"

export default class Child extends React.Component{

    clickHandle = (e) =>{
        this.props.onMyEvent("父标题");
    }
    
    render(){
        return(
            <div>
                Child:{ this.props.title }
                <button onClick={ this.clickHandle }>传递数据</button>
            </div>
        )
    }
}

App.js

import React from 'react';
import Parent from "./components/coms/Parent"
import { connect } from "react-redux"
// import { increment,decrement } from "./actions/counter"
import * as counterActions from "./actions/counter"
import { bindActionCreators } from "redux"
import User from "./components/user"

class App extends React.Component {
    render(){
        return (
            <div className="container">
                {/* <Parent/> */}
                <h1 className="jumbotron-heading text-center">{ this.props.counter }</h1>
                <p className="text-center">
                    {/* <button onClick={ this.props.onIncrement } className="btn btn-primary">increment</button>
                    <button onClick={ this.props.onDecrement } className="btn btn-success">decrement</button> */}
                    {/* <button onClick={ () => (increment()) } className="btn btn-primary">increment</button>
                    <button onClick={ () => (decrement()) } className="btn btn-success">decrement</button> */}
                    <button onClick={ () => this.props.counterActions.increment(10) } className="btn btn-primary">increment</button>
                    <button onClick={ () => this.props.counterActions.decrement(5) } className="btn btn-success">decrement</button>
                </p>
                <User />
            </div>
        );
    }
}

const mapStateToProps = (state) =>{
    console.log(state);
    
    return{
        counter:state.counter
    }
}

// const mapDispatchToProps = (dispatch) =>{
//     return {
//         increment: () => { dispatch(increment()) },
//         decrement: () => { dispatch(decrement()) }
//     }
// }

const mapDispatchToProps = (dispatch) =>{
    return{
        counterActions:bindActionCreators(counterActions,dispatch)
    }
}

// 先后顺序不能颠倒
export default connect(mapStateToProps,mapDispatchToProps)(App)

index.js

createStore

React从入门到实战- 企业级实战项目-宜居二_App_13

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore,applyMiddleware } from "redux"
import { composeWithDevTools } from 'redux-devtools-extension';
import logger from "redux-logger"
import thunk from "redux-thunk"
// import reducer from "./reducers/counter"
import { Provider } from "react-redux"
import rootReducer from "./reducers"

// 异步和同步
// 定时器  网络请求


// 中间件
// const logger = store  => next => action =>{
//     console.log("dispatch->",action);
//     let result = next(action); // 加载下一个中间件
//     console.log("next state->",store.getState());
//     return result;
// }

// const error = store => next => action => {
//     try{
//         next(action)
//     }catch(e){
//         console.log("error->",e);
//     }
// }

// 创建store仓库
// const store = createStore(rootReducer,{},applyMiddleware(logger,error));
const store = createStore(rootReducer,{},composeWithDevTools(applyMiddleware(logger,thunk)));
// store.subscribe(() => console.log("state:", store.getState()))

// const render = () => {
//     ReactDOM.render(
//         <App
//             onIncrement={() => store.dispatch({ type: "INCREMENT" })}
//             onDecrement={() => store.dispatch({ type: "DECREMENT" })}
//             value={store.getState()}
//         />,
//         document.getElementById('root'));

// }
// render();
// store.subscribe(render)




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

42.BootStrap CDN加载

(BootStrap CDN加载&)

BootStrap CDN加载&

CDN加载

React从入门到实战- 企业级实战项目-宜居二_App_14

引入CDN

React从入门到实战- 企业级实战项目-宜居二_App_15

编写视图

React从入门到实战- 企业级实战项目-宜居二_数据_16

React从入门到实战- 企业级实战项目-宜居二_react_17

react-redux-demo\public\index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="logo192.png" />
    <!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
  </body>
</html>

43.Redux 引入Redux

(Redux 引入Redux&)

Redux 引入Redux&

React从入门到实战- 企业级实战项目-宜居二_react_18

React从入门到实战- 企业级实战项目-宜居二_ico_19

Redux应用的是单一的stroe

React从入门到实战- 企业级实战项目-宜居二_数据_20

Reducer存在stroe中改变状态的方案

React从入门到实战- 企业级实战项目-宜居二_数据_21

Reducer写法,state往仓库中存的状态,默认state为0,action.type不同的状态改变state

React从入门到实战- 企业级实战项目-宜居二_ico_22

Store.dispatch触发来更新state的

React从入门到实战- 企业级实战项目-宜居二_react_23

reducer点击触发action.type,在App接收触发两个事件,action动作要对应到reducer一样的字符。

React从入门到实战- 企业级实战项目-宜居二_数据_24

React从入门到实战- 企业级实战项目-宜居二_App_25

监听数据的变化,store.getState读取到数据

subscribe监听数据变化

React从入门到实战- 企业级实战项目-宜居二_数据_26

React从入门到实战- 企业级实战项目-宜居二_数据_27

React从入门到实战- 企业级实战项目-宜居二_数据_28

渲染到页面上

React从入门到实战- 企业级实战项目-宜居二_App_29

React从入门到实战- 企业级实战项目-宜居二_react_30

要重新渲染,首先要render()以后每次监听数据变化要重新渲染render

重新渲染render

React从入门到实战- 企业级实战项目-宜居二_react_31

src\reducers\counter.js

reducer

import * as actions from "../constants"

const counter = ( state = 0,action ) => {
    switch(action.type){
        case actions.INCREMENT:
            // throw new Error("error reducer")
            return state + action.num;
        case actions.DECREMENT:
            return state - action.num;
        default:
            return state;
    }
}

export default counter

src\constants\index.js

// counter
export const INCREMENT = "INCREMENT"
export const DECREMENT = "DECREMENT"

// user
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS"
export const FETCH_USER_REQUEST = "FETCH_USER_REQUEST";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";

44.Redux 引入React-Redux与mapStateToProps读取数据

(Redux 引入React-Redux与mapStateToProps读取数据&)

Redux 引入React-Redux与mapStateToProps读取数据&

组件通过Mouse触发到Provider身上形成多个Actions动作,触发store当中的reducer,reducer就会发生改变,有多少个action就会对应多少store和reducer

React从入门到实战- 企业级实战项目-宜居二_数据_32

React从入门到实战- 企业级实战项目-宜居二_react_33

Provider用来包裹的关联上redux和react,不要任何的操作了,单纯应用App,需要store对象

React从入门到实战- 企业级实战项目-宜居二_数据_34

通过Provider本身额外的操作,store是刚刚创建的store,通过provider关联react和redux

react-redux

React从入门到实战- 企业级实战项目-宜居二_react_35

组件中读取数据

通过connect把App组件与redux连接起来。

按钮操作的两个事件,外面没有传递操作事件了。

mapStateToProps对象读取数据的方案,通过state参数,counter中能读到数据,放到connect()中。Connect连接组件与redux,读取数据

mapStateToProps读取数据

React从入门到实战- 企业级实战项目-宜居二_数据_36

State本身就是counter的值

React从入门到实战- 企业级实战项目-宜居二_数据_37

React从入门到实战- 企业级实战项目-宜居二_ico_38

React从入门到实战- 企业级实战项目-宜居二_react_39

读取数据要从props中读到数据

React从入门到实战- 企业级实战项目-宜居二_数据_40

45.Redux dispatch与mapDispatchToProps修改数据方案

(Redux dispatch与mapDispatchToProps修改数据方案&)

Redux dispatch与mapDispatchToProps修改数据方案&

操作和修改数据通过dispatch,要单独创建action操作,要提取action操作方便后期维护

React从入门到实战- 企业级实战项目-宜居二_ico_41

Return type值

React从入门到实战- 企业级实战项目-宜居二_ico_42

引入action的操作对象

React从入门到实战- 企业级实战项目-宜居二_App_43

mapDispatchToProps用来分发事件的,dispatch触发事件的

注意Connect的顺序不能颠倒

dispatch触发action

React从入门到实战- 企业级实战项目-宜居二_react_44

Props中有了increment

React从入门到实战- 企业级实战项目-宜居二_react_45

通过dispatch触发increment()函数,需要props

React从入门到实战- 企业级实战项目-宜居二_react_46

没有dispatch可以触发函数,props中有increment函数

React从入门到实战- 企业级实战项目-宜居二_ico_47

src\actions\counter.js

import * as actions from "../constants"
// export function increment(num){
//     return{
//         type:actions.INCREMENT,
//         num
//     }
// }

export function increment(num){
    return dispatch => {
        setTimeout(() =>{
            dispatch({
                type:actions.INCREMENT,
                num
            })
        },1000)
    }
}


export function decrement(num){
    return{
        type:actions.DECREMENT,
        num
    }
}

46.Redux bindActionCreators与参数传递

(Redux bindActionCreators与参数传递&)

Redux bindActionCreators与参数传递&

Action中有很多的操作另一种方式,写起来更容易一些

React从入门到实战- 企业级实战项目-宜居二_数据_48

mapDispatchToProps操作方法变简单了,通过bindActionCreators绑定所有的actions

React从入门到实战- 企业级实战项目-宜居二_App_49

操作方法简单了,操作按钮修改,通过this.props.counterActions中的increment方法操作,事件触发不是触发函数了。

React从入门到实战- 企业级实战项目-宜居二_App_50

方法的字符串要有reducer的action.type对应,不用写非常多的actions动作。

React从入门到实战- 企业级实战项目-宜居二_数据_51

字符串要对应,通过常量文件夹管理。

React从入门到实战- 企业级实战项目-宜居二_数据_52

React从入门到实战- 企业级实战项目-宜居二_ico_53

常量字符串,action参数传递

React从入门到实战- 企业级实战项目-宜居二_App_54

直接引用

React从入门到实战- 企业级实战项目-宜居二_数据_55

React从入门到实战- 企业级实战项目-宜居二_react_56

按照指定数字增加与减少,参数传递,参数传递给了action,操作的是actions

React从入门到实战- 企业级实战项目-宜居二_react_57

真正操作的是reducer,前后一样可以省略

React从入门到实战- 企业级实战项目-宜居二_数据_58

参数传递,直接通过前台数据传递到后台actions,通过actions响应到reducers,接收数据

React从入门到实战- 企业级实战项目-宜居二_App_59

src\actions\counter.js

import * as actions from "../constants"
// export function increment(num){
//     return{
//         type:actions.INCREMENT,
//         num
//     }
// }

export function increment(num){
    return dispatch => {
        setTimeout(() =>{
            dispatch({
                type:actions.INCREMENT,
                num
            })
        },1000)
    }
}


export function decrement(num){
    return{
        type:actions.DECREMENT,
        num
    }
}

src\reducers\counter.js

import * as actions from "../constants"

const counter = ( state = 0,action ) => {
    switch(action.type){
        case actions.INCREMENT:
            // throw new Error("error reducer")
            return state + action.num;
        case actions.DECREMENT:
            return state - action.num;
        default:
            return state;
    }
}

export default counter

47.Redux combineReducers合并reducer

(Redux combineReducers合并reducer&)

Redux combineReducers合并reducer&

State中有很多条数据,很多数据需要共享,要合并reducer

React从入门到实战- 企业级实战项目-宜居二_App_60

通过combineReducers来合并reducers

React从入门到实战- 企业级实战项目-宜居二_App_61

外层引用关系不需要引用reducer了,直接引用rootReducers

React从入门到实战- 企业级实战项目-宜居二_App_62

此时相当于给counter多加了一个对象,state代表一个对象,要读取state里面的counter

React从入门到实战- 企业级实战项目-宜居二_App_63

新增User组件在App中引用

React从入门到实战- 企业级实战项目-宜居二_react_64

React从入门到实战- 企业级实战项目-宜居二_react_65

User数据也是共享的,为User也创建一系列的数据

React从入门到实战- 企业级实战项目-宜居二_App_66

React从入门到实战- 企业级实战项目-宜居二_App_67

State中有多条数据了

React从入门到实战- 企业级实战项目-宜居二_数据_68

React从入门到实战- 企业级实战项目-宜居二_App_69

combineReducers合并reducers

src\reducers\index.js

import { combineReducers } from "redux"
import counter from "./counter"
import user from "./user"

const rootReducer = combineReducers({
    counter,
    user
})

export default rootReducer

src\reducers\user.js

import { FETCH_USER_SUCCESS,FETCH_USER_REQUEST, FETCH_USER_FAILURE } from "../constants"

const initialState = {
    user:{},
    isFetching: false,
    error: null,
}

const user = ( state = initialState,action ) => {
    switch(action.type){
        case FETCH_USER_SUCCESS:
            return {
                isFetching: false,
                error: null,
                user: action.user
            };
        case FETCH_USER_REQUEST:
            return {
                isFetching: true,
                error: null,
                user: {}
            }
        case FETCH_USER_FAILURE:
            return {
                isFetching: false,
                error: action.error,
                user: {}
            };
        default:
            return state;
    }
}

export default user

App.js

import React from 'react';
import Parent from "./components/coms/Parent"
import { connect } from "react-redux"
// import { increment,decrement } from "./actions/counter"
import * as counterActions from "./actions/counter"
import { bindActionCreators } from "redux"
import User from "./components/user"

class App extends React.Component {
    render(){
        return (
            <div className="container">
                {/* <Parent/> */}
                <h1 className="jumbotron-heading text-center">{ this.props.counter }</h1>
                <p className="text-center">
                    {/* <button onClick={ this.props.onIncrement } className="btn btn-primary">increment</button>
                    <button onClick={ this.props.onDecrement } className="btn btn-success">decrement</button> */}
                    {/* <button onClick={ () => (increment()) } className="btn btn-primary">increment</button>
                    <button onClick={ () => (decrement()) } className="btn btn-success">decrement</button> */}
                    <button onClick={ () => this.props.counterActions.increment(10) } className="btn btn-primary">increment</button>
                    <button onClick={ () => this.props.counterActions.decrement(5) } className="btn btn-success">decrement</button>
                </p>
                <User />
            </div>
        );
    }
}

const mapStateToProps = (state) =>{
    console.log(state);
    
    return{
        counter:state.counter
    }
}

// const mapDispatchToProps = (dispatch) =>{
//     return {
//         increment: () => { dispatch(increment()) },
//         decrement: () => { dispatch(decrement()) }
//     }
// }

const mapDispatchToProps = (dispatch) =>{
    return{
        counterActions:bindActionCreators(counterActions,dispatch)
    }
}

// 先后顺序不能颠倒
export default connect(mapStateToProps,mapDispatchToProps)(App)

user.jsx

import React from "react"
import { connect } from "react-redux"
import { bindActionCreators } from "redux"
import * as userActions from "../actions/user"

class User extends React.Component{

    render(){

        const { error, isFetching, user } = this.props.user;

        let data;

        if (error) {
            data = error;
        } else if (isFetching) {
            data = "Loading...";
        } else {
            data = user.title;
        }

        return(
            <div className="continer text-center">
                <p className="text-center">{ data }</p>
                <p className="text-center">User</p>
                <button className="btn btn-primary" onClick={ () => { this.props.userActions.get_user() } }>getUser</button>
            </div>
        )
    }
}

const mapStateToProps = (state) =>{
    return{
        user:state.user
    }
}

const mapDispatchToProps = (dispatch) =>{
    return {
        userActions:bindActionCreators(userActions,dispatch)
    }
}

export default connect(mapStateToProps,mapDispatchToProps)(User)

48.Redux Redux中间件与第三方中间件logger

(Redux Redux中间件与第三方中间件logger&)

Redux Redux中间件与第三方中间件logger&

logger中间件

React从入门到实战- 企业级实战项目-宜居二_ico_70

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore,applyMiddleware } from "redux"
import { composeWithDevTools } from 'redux-devtools-extension';
import logger from "redux-logger"
import thunk from "redux-thunk"
// import reducer from "./reducers/counter"
import { Provider } from "react-redux"
import rootReducer from "./reducers"

// 异步和同步
// 定时器  网络请求


// 中间件
// const logger = store  => next => action =>{
//     console.log("dispatch->",action);
//     let result = next(action); // 加载下一个中间件
//     console.log("next state->",store.getState());
//     return result;
// }

// const error = store => next => action => {
//     try{
//         next(action)
//     }catch(e){
//         console.log("error->",e);
//     }
// }

// 创建store仓库
// const store = createStore(rootReducer,{},applyMiddleware(logger,error));
const store = createStore(rootReducer,{},composeWithDevTools(applyMiddleware(logger,thunk)));
// store.subscribe(() => console.log("state:", store.getState()))

// const render = () => {
//     ReactDOM.render(
//         <App
//             onIncrement={() => store.dispatch({ type: "INCREMENT" })}
//             onDecrement={() => store.dispatch({ type: "DECREMENT" })}
//             value={store.getState()}
//         />,
//         document.getElementById('root'));

// }
// render();
// store.subscribe(render)




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

React从入门到实战- 企业级实战项目-宜居二_ico_71

49.Redux Redux异步中间件redux-thunk

(Redux Redux异步中间件redux-thunk&)

Redux Redux异步中间件redux-thunk&

处理数据前经历中间件的处理,再去处理数据,增加一层防护

React从入门到实战- 企业级实战项目-宜居二_数据_72

单一化写法

React从入门到实战- 企业级实战项目-宜居二_App_73

用户操作记录,next加载中间件,中间件要放在创建仓库store的上面,所有用户操作都会加载中间件

React从入门到实战- 企业级实战项目-宜居二_react_74

可以有多个中间件

React从入门到实战- 企业级实战项目-宜居二_react_75

Throw 抛出错误

React从入门到实战- 企业级实战项目-宜居二_react_76

常用的中间件系统都已经提供了

React从入门到实战- 企业级实战项目-宜居二_react_77

React从入门到实战- 企业级实战项目-宜居二_数据_78

src\actions\counter.js

redux-thunk异步

import * as actions from "../constants"
// export function increment(num){
//     return{
//         type:actions.INCREMENT,
//         num
//     }
// }

export function increment(num){
    return dispatch => {
        setTimeout(() =>{
            dispatch({
                type:actions.INCREMENT,
                num
            })
        },1000)
    }
}


export function decrement(num){
    return{
        type:actions.DECREMENT,
        num
    }
}

React从入门到实战- 企业级实战项目-宜居二_数据_79

src\reducers\users.js

reducer 等待loading状态

import { FETCH_USER_SUCCESS,FETCH_USER_REQUEST, FETCH_USER_FAILURE } from "../constants"

const initialState = {
    user:{},
    isFetching: false,
    error: null,
}

const user = ( state = initialState,action ) => {
    switch(action.type){
        case FETCH_USER_SUCCESS:
            return {
                isFetching: false,
                error: null,
                user: action.user
            };
        case FETCH_USER_REQUEST:
            return {
                isFetching: true,
                error: null,
                user: {}
            }
        case FETCH_USER_FAILURE:
            return {
                isFetching: false,
                error: action.error,
                user: {}
            };
        default:
            return state;
    }
}

export default user

src\actions\users.js

thunk网络异步请求,dispatch触发传递数据

import { FETCH_USER_SUCCESS,FETCH_USER_FAILURE,FETCH_USER_REQUEST } from "../constants"

export const fetch_user_failure = (error) => {
    return {
        type: FETCH_USER_FAILURE,
        error
    };
};

export const fetch_user = (user) => {
    return {
        type: FETCH_USER_SUCCESS,
        user
    }
};

export const fetch_user_request = () => {
    return {
        type: FETCH_USER_REQUEST
    }
};


export const get_user = () =>{
    return dispatch =>{
        dispatch(fetch_user_request())
        fetch("http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php")
        .then(res => res.json())
        .then(data =>{
            dispatch(fetch_user(data.chengpinDetails[0]))
        })
        .catch(error =>{
            dispatch(fetch_user_failure(error));
        })
    }
}

user.jsx

div的className调整

import React from "react"
import { connect } from "react-redux"
import { bindActionCreators } from "redux"
import * as userActions from "../actions/user"

class User extends React.Component{

    render(){

        const { error, isFetching, user } = this.props.user;

        let data;

        if (error) {
            data = error;
        } else if (isFetching) {
            data = "Loading...";
        } else {
            data = user.title;
        }

        return(
            <div className="continer text-center">
                <p className="text-center">{ data }</p>
                <p className="text-center">User</p>
                <button className="btn btn-primary" onClick={ () => { this.props.userActions.get_user() } }>getUser</button>
            </div>
        )
    }
}

const mapStateToProps = (state) =>{
    return{
        user:state.user
    }
}

const mapDispatchToProps = (dispatch) =>{
    return {
        userActions:bindActionCreators(userActions,dispatch)
    }
}

export default connect(mapStateToProps,mapDispatchToProps)(User)

React从入门到实战- 企业级实战项目-宜居二_数据_80

50.Redux Redux-thunk网络请求

(Redux Redux-thunk网络请求&)

Redux Redux-thunk网络请求&

Thunk主要用于处理异步,异步操作有定时器和网络请求

希望延时1s增加值,无论是触发action,都是同一个action,把异步写在aciton中。

Dispatch触发事件。

React从入门到实战- 企业级实战项目-宜居二_数据_81

需要处理异步

React从入门到实战- 企业级实战项目-宜居二_App_82

React从入门到实战- 企业级实战项目-宜居二_react_83

引入异步中间件

React从入门到实战- 企业级实战项目-宜居二_ico_84

React从入门到实战- 企业级实战项目-宜居二_ico_85

网络异步请求

React从入门到实战- 企业级实战项目-宜居二_数据_86

修改reducers,修改state的初始,提取出来。State的操作不能直接改变state,success的时候return {}对象从action中读取user对象。

React从入门到实战- 企业级实战项目-宜居二_react_87

State是只读的,要改变需要返回一个新的state

React从入门到实战- 企业级实战项目-宜居二_react_88

修改action,单独进行异步操作return dispatch在回调中进行异步操作。

把数据给fetch_user里面,通过dispatch触发。Get_USER前台可以直接调用

React从入门到实战- 企业级实战项目-宜居二_数据_89

React从入门到实战- 企业级实战项目-宜居二_App_90

通过connect关联redux,最后没有分号,可以读取数据

React从入门到实战- 企业级实战项目-宜居二_App_91

数据来源于user中的title,通过点击按钮读取数据,通过动作方法来读取数据。

点击user数据发生变化。

React从入门到实战- 企业级实战项目-宜居二_react_92

React从入门到实战- 企业级实战项目-宜居二_数据_93

51.Redux Redux-thunk请求三种状态

(Redux Redux-thunk请求三种状态&)

Redux Redux-thunk请求三种状态&

网络请求延时,加载等待的过程,调整慢,isFetching等待状态

React从入门到实战- 企业级实战项目-宜居二_数据_94

React从入门到实战- 企业级实战项目-宜居二_ico_95

Actions也对应有三种等待状态

React从入门到实战- 企业级实战项目-宜居二_App_96

视图中响应,三种状态都赋值给data,只用加载data即可

React从入门到实战- 企业级实战项目-宜居二_react_97

React从入门到实战- 企业级实战项目-宜居二_数据_98

视图中loading的状态,检查debugger返回是否为true

React从入门到实战- 企业级实战项目-宜居二_ico_99

调用等待

React从入门到实战- 企业级实战项目-宜居二_数据_100

异步都需要dispatch触发,否则无法传递数据

React从入门到实战- 企业级实战项目-宜居二_数据_101

user.js

import { FETCH_USER_SUCCESS,FETCH_USER_REQUEST, FETCH_USER_FAILURE } from "../constants"

const initialState = {
    user:{},
    isFetching: false,
    error: null,
}

const user = ( state = initialState,action ) => {
    switch(action.type){
        case FETCH_USER_SUCCESS:
            return {
                isFetching: false,
                error: null,
                user: action.user
            };
        case FETCH_USER_REQUEST:
            return {
                isFetching: true,
                error: null,
                user: {}
            }
        case FETCH_USER_FAILURE:
            return {
                isFetching: false,
                error: action.error,
                user: {}
            };
        default:
            return state;
    }
}

export default user

src\actions\user.js

import { FETCH_USER_SUCCESS,FETCH_USER_FAILURE,FETCH_USER_REQUEST } from "../constants"

export const fetch_user_failure = (error) => {
    return {
        type: FETCH_USER_FAILURE,
        error
    };
};

export const fetch_user = (user) => {
    return {
        type: FETCH_USER_SUCCESS,
        user
    }
};

export const fetch_user_request = () => {
    return {
        type: FETCH_USER_REQUEST
    }
};


export const get_user = () =>{
    return dispatch =>{
        dispatch(fetch_user_request())
        fetch("http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php")
        .then(res => res.json())
        .then(data =>{
            dispatch(fetch_user(data.chengpinDetails[0]))
        })
        .catch(error =>{
            dispatch(fetch_user_failure(error));
        })
    }
}
// counter
export const INCREMENT = "INCREMENT"
export const DECREMENT = "DECREMENT"

// user
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS"
export const FETCH_USER_REQUEST = "FETCH_USER_REQUEST";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";

52.Redux Redux调试工具

(Redux Redux调试工具&)

Redux Redux调试工具&

检测redux数据的变化,在Chrome中应用安装即可

React从入门到实战- 企业级实战项目-宜居二_数据_102

通过依赖来启动插件

React从入门到实战- 企业级实战项目-宜居二_App_103

引用到主入口文件,包裹整个插件

React从入门到实战- 企业级实战项目-宜居二_App_104

React从入门到实战- 企业级实战项目-宜居二_App_105

React从入门到实战- 企业级实战项目-宜居二_ico_106

React从入门到实战- 企业级实战项目-宜居二_App_107

React从入门到实战- 企业级实战项目-宜居二_react_108

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore,applyMiddleware } from "redux"
import { composeWithDevTools } from 'redux-devtools-extension';
import logger from "redux-logger"
import thunk from "redux-thunk"
// import reducer from "./reducers/counter"
import { Provider } from "react-redux"
import rootReducer from "./reducers"

// 异步和同步
// 定时器  网络请求


// 中间件
// const logger = store  => next => action =>{
//     console.log("dispatch->",action);
//     let result = next(action); // 加载下一个中间件
//     console.log("next state->",store.getState());
//     return result;
// }

// const error = store => next => action => {
//     try{
//         next(action)
//     }catch(e){
//         console.log("error->",e);
//     }
// }

// 创建store仓库
// const store = createStore(rootReducer,{},applyMiddleware(logger,error));
const store = createStore(rootReducer,{},composeWithDevTools(applyMiddleware(logger,thunk)));
// store.subscribe(() => console.log("state:", store.getState()))

// const render = () => {
//     ReactDOM.render(
//         <App
//             onIncrement={() => store.dispatch({ type: "INCREMENT" })}
//             onDecrement={() => store.dispatch({ type: "DECREMENT" })}
//             value={store.getState()}
//         />,
//         document.getElementById('root'));

// }
// render();
// store.subscribe(render)




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

(React 进阶&)

React 进阶&

1.React 进阶 组件优化1

(组件优化1&)

组件优化1&

React从入门到实战- 企业级实战项目-宜居二_ico_109

React从入门到实战- 企业级实战项目-宜居二_App_110

parent.jsx

外部需要拿到count的值通过回调函数

回调函数获取定时器中的值,定时器网络请求组件销毁处理

state变化render重新渲染pureComponent

import React from "react"
import Child from "./child"
import Child1 from "./child1"


/**
 * 计数例子
 *  
 *  定时器
 *  网络请求
 *  事件监听
 *    在组件被销毁前都应得到相应的处理
 * 
 */

const MyAPI = {
    count:0,
    subscribe(cb){
        this.intervalId = setInterval(() =>{
            this.count += 1
            cb(this.count);
        },1000)
    },
    unSubscribe(){
        clearInterval(this.intervalId);
        this.reset();
    },
    reset(){
        this.count = 0;
    }
}

export default class Parent extends React.Component {

    state = {
        count:0
    }

    componentDidMount(){
        MyAPI.subscribe((currentCount) =>{
            this.setState({
                count:currentCount
            })
        })
    }

    componentWillUnmount(){
        MyAPI.unSubscribe();
    }

    render() {
        console.log("parent -> render");
        return (
            <div>
                Parent:{ this.state.count }
                <Child num={ this.state.count }/>
                <Child1 num={ 1 }/>
            </div>
        )
    }
}

child1.jsx 不通过生命周期方式进行处理

import React from 'react';


/**
 * Component:不会对数据进行比较
 * PureComponent:对数据进行浅比较 props
 * 
 */
export default class Child1 extends React.PureComponent {

    render() {
        console.log("child1 -> render");
        return (
            <div>
                Child1:{ this.props.num }
            </div>
        );
    }
}

child.jsx state发生改变render重新渲染,整个Child重新渲染,通过生命周期Child不用渲染

shouldComponentUpdate

import React from 'react';

export default class Child extends React.Component {

    /**
     * 
     * @param {*} nextProps 
     * @param {*} nextState 
     * 
     * 关于渲染问题
     */
    shouldComponentUpdate(nextProps,nextState){
        if(nextProps.num === this.props.num){
            return false;
        }
        return true;
    }

    render() {
        console.log("child -> render");
        return (
            <div>
                Child:{ this.props.num }
            </div>
        );
    }
}

App.js

HashRouter

import React from 'react';
import Demo1 from "./components/Demo1/parent"
import Home from "./components/Home"
import { HashRouter, Route, Switch } from "react-router-dom"

function App() {
    return (
        <HashRouter>
            <Switch>
                <Route exact path="/" component={ Home }></Route>
                <Route path="/demo1" component={ Demo1 }></Route>
            </Switch>
        </HashRouter>
    );
}

export default App;

Home.jsx

import React from 'react';
import Demo2 from "./Demo2"
import Demo3 from "./Demo3"
import Demo4 from "./Demo4"

import Banner from "./Demo5/banner"
import Chengpin from "./Demo5/chengpin"

import NewBanner from "./Demo5/New/NewBanner"
import NewChengpin from "./Demo5/New/NewChengpin"

import Demo6 from "./Demo6/Parent"

function Home() {
    return (
        <div>
            <Demo2 />
            <Demo3 />
            <Demo4 />

            <Banner />
            <Chengpin />

            <NewBanner />
            <NewChengpin />

            <Demo6 />
        </div>
    );
}

export default Home;

React从入门到实战- 企业级实战项目-宜居二_App_111

2.React 进阶 组件优化2

(组件优化2&)

组件优化2&

index.jsx,列表数据动态生成

Fragment返回li

import React,{ Fragment } from "react"

class Item extends React.Component {
    render() {
        return (
            <Fragment>
                <li>Demo2 Item1</li>
                <li>Demo2 Item1</li>
            </Fragment>
        )
    }
}


export default class Demo2 extends React.Component {
    render() {
        return (
            <ul>
                <Item />
            </ul>
        )
    }
}

React从入门到实战- 企业级实战项目-宜居二_ico_112

3.React 进阶 组件优化3

(组件优化3&)

组件优化3&

index.jsx color只有在comment中使用,context上下文对象,getChildContext系统提供方法,context对象直接传递history不用两次传递

context孙子组件通信传递history

import React from "react"
import PropTypes from "prop-types"

const Topic = (props) =>{
    return(
        <div>
            <Comment/>
        </div>
    )
}

/**
 * 
 * 路由:this.props.history.push("/"):必须是路由直接子元素,如果是孙子元素,则不能使用
 * 
 * withRouter
 * 
 */

const Comment = (props,context) =>{
    return(
        <div>
            { context.color }
        </div>
    )
}

export default class Demo3 extends React.Component{

    getChildContext(){
        return{
            color:"red"
        }
    }

    render(){
        return(
            <div>
                <Topic/>
            </div>
        )
    }
}

Comment.contextTypes = {
    color:PropTypes.string
}
Demo3.childContextTypes = {
    color:PropTypes.string
}

4.React 进阶 Fragment

(React 进阶 Fragment&)

React 进阶 Fragment&

5.React 进阶 Context

(React 进阶 Context&)

React 进阶 Context&

6.React 进阶 高阶组件

(React 进阶 高阶组件&)

React 进阶 高阶组件&

index.jsx 可以在高阶组件中对MyData进行处理

高阶组件定义,

import React from "react"

/**
 * 
 * 高阶组件
 *  1.函数
 *  2.参数一个组件
 *  3.返回值也是一个组件
 */

const withFetch = (ComposeComponent) =>{
    return class extends React.Component{
        render(){
            return(
                <ComposeComponent {...this.props} />
            )
        }
    }
}

class MyData extends React.Component{
    render(){
        return(
            <div>
                MyData:{this.props.data}
            </div>
        )
    }
}

const WithFetch = withFetch(MyData);

export default class Demo4 extends React.Component{
    render(){
        return(
            <div>
                <WithFetch data={"Hello WithFetch"}/>
            </div>
        )
    }
}

7.React 进阶 高阶组件应用

(React 进阶 高阶组件应用&)

React 进阶 高阶组件应用&

banner组件与chengpin组件出了url不同其他都一样

相似组件通过高阶组件优化处理

banner.jsx

import React, { Component } from 'react';

class Banner extends Component {
  constructor() {
    super();
    this.state = {
      loading: true,
      banner: null
    };
  }

  componentDidMount() {
    fetch('http://iwenwiki.com/api/blueberrypai/getIndexBanner.php')
      .then(res => res.json())
      .then(banner => {
        this.setState({
          loading: false,
          banner: banner
        });
      })
  }

  render() {
    if(this.state.loading) {
      return (
        <div>loading</div>
      )
    } else {
      return (
        <h1>{this.state.banner.banner[0].title}</h1>
      )
    }
  }
}

export default Banner;

chengpin.jsx

import React, { Component } from 'react';

class Chengpin extends Component {
  constructor() {
    super();
    this.state = {
      loading: true,
      chengpin: null
    };
  }

  componentDidMount() {
    fetch('http://iwenwiki.com/api/blueberrypai/getChengpinInfo.php')
      .then(res => res.json())
      .then(chengpin => {
        this.setState({
          loading: false,
          chengpin: chengpin
        });
      })
  }

  render() {
    if(this.state.loading) {
      return (
        <div>loading</div>
      )
    } else {
      return (
        <h1>{this.state.chengpin.chengpinInfo[0].title}</h1>
      )
    }
  }
}

export default Chengpin;

withFetch.jsx return两个函数

import React,{Component} from 'react'

const withFetch = (url) => (View) => {
    return class extends Component {
        constructor() {
            super();
            this.state = {
                loading: true,
                data: null
            }
        }

        componentDidMount() {
            fetch(url)
                .then(res => res.json())
                .then(data => {
                    this.setState({
                        loading: false,
                        data: data
                    });
                })
        }

        render(){
            if(this.state.loading){
                return(
                    <div>loadding....</div>
                )
            }else{
                return <View data={ this.state.data }></View>
            }
        }

    }
}

export default withFetch

NewBanner.jsx,有两个参数第二个参数是组建视图,相同得到放到高阶组件处理

import React from "react"
import withFetch from "../withFetch"

const Banner = withFetch("http://iwenwiki.com/api/blueberrypai/getIndexBanner.php")(props =>{
    return(
        <div>
            <p>
                { props.data.banner[0].title }
            </p>
        </div>
    )
})

export default Banner;

NewChengpin.jsx

import React from "react"
import withFetch from "../withFetch"

const Chengpin = withFetch("http://iwenwiki.com/api/blueberrypai/getChengpinInfo.php")(props =>{
    return(
        <div>
            <p>
                { props.data.chengpinInfo[0].title }
            </p>
        </div>
    )
})

export default Chengpin;

React从入门到实战- 企业级实战项目-宜居二_App_113

8.React 进阶 错误边界处理

(React 进阶 错误边界处理&)

React 进阶 错误边界处理&

Parent.jsx

import React from 'react'
import Errors from "./Errors"
import ErrorBoundary from "./ErrorBoundary"

export default class Parent extends React.Component{

    state = {
        count:0
    }

    increment = () =>{
        this.setState({
            count:this.state.count+1
        })
    }


    decrement = () =>{
        this.setState({
            count:this.state.count-1
        })
    }


    render(){
        return(
            <div>
                <h3>这是一个标题</h3>
                <p>{ this.state.count }</p>
                <ErrorBoundary render={ (error,errorInfo) => <p>{ '加载时发生错误' }</p> }>
                    <Errors />
                </ErrorBoundary>
                <button onClick={ this.increment }>Increment</button>
                <button onClick={ this.decrement }>Decrement</button>
            </div>
        )
    }
}

Errors.jsx

错误边界componentDidCatch

import React from 'react'

export default class Errors extends React.Component{

    render(){
        return(
            <ul>
                {
                    null.map((element,index) =>{
                        return <li key={index} >{ element }</li>
                    })
                }
            </ul>
        )
    }

}

ErrorBoundary.jsx

import React from "react"

export default class ErrorBoundary extends React.Component{
    state = {
        hasError:false,
        error:null,
        errorInfo:null
    }

    /**
     * 子元素发生错误时触发
     */

    componentDidCatch(error,errorInfo){
        this.setState({
            hasError:true,
            error:error,
            errorInfo:errorInfo
        })
    }

    render(){
        if(this.state.hasError){
            return <div>{ this.props.render(this.state.error,this.state.errorInfo) }</div>
        }
        return this.props.children;
    }
}

(React&Redux实战-登录注册认证&)

React&Redux实战-登录注册认证&

(React 企业级实战项目-宜居&)

React 企业级实战项目-宜居&