Redux and asynchronous operations middleware

  Recall Redux data transfer, the user clicks the button to send an action, reducer will be calculated according to the new state and previous state action, the callback function store.subscribe method store.getState () to obtain a new state, the state injected into the page elements to achieve the status update page. You simply do not find the opportunity to do an asynchronous operation, how to do? The real world there are a large number of asynchronous operations that use the new tool Redux: The middleware.

  Middleware, plainly, is the middle part of the normal process, perform a function, the function b is then performed, but this time, in the middle of a and b, a middleware function is inserted, the flow becomes, to perform a function, then execute the middleware, the final implementation b function, if an error occurs in the course of the middleware execution, there is no chance that b function is executed, you can see the emergence of middleware, to hinder a b order of execution , then this time, we can make a series of things in the middleware, such as logging, asynchronous processing. Specific to the Redux is, in the normal flow of Redux, send an action, it will immediately to the reducer, then we will send in action and inserted into the middleware between reducer, stop this sequence of operations, we are in these middleware for asynchronous operation and the like. Middleware how to insert it? Redux provides a applyMiddleware function, the middleware to be used as a parameter passed to it just fine, applyMiddleware function calls in createStore time. In order to make better use of middleware, pure html is not a good way of demonstration, where a simple configuration webpack, using only webapck-dev-server on it.

  Open a command-line tool (git bash) in any folder, mkdir redux-middleware && cd redux-middleware && npm init -y quickly create a project. npm i webpack webpack-dev-server webpack-cli -D installation webpack-dev-server. webpack4 provides zero-configuration, the default file is the entrance index.js src directory, to create src directory and below it index.js . Since webpack-dev-server package is to file into memory, after the packaged file into the root directory of main.js, so main.js do not build, but to build a html file their introduction, last npm instail redux redux- thunk, redux-logger -S. redux-thunk for asynchronous requests, redux-logger logs.

  redux-thunk as middleware asynchronous request, using a very simple manner, when a dispatch function, when it performs this function, and not transmitted to the reducer, if only one object dispatch time, it will transfer to the reducer in change of state. That is, when the asynchronous request to dispatch a first function (action creator function to return a), there is a dispatch function as a parameter, and then within the function, such as access to the data or the error, then dispatch an object, to pass the acquired data or error information to the reducer, thereby changing the State, the update data is completed.

   Now do a simple example, is to click the button, random access to information a user of the Internet has such a api interfaces https://randomuser.me/api/?results=1, the latter result = 1 represents a once only get, is a get request, the returned result (JSON format) as follows been truncated to show only the name, geneder, email, and img

{
    "results": [
        {
            "gender": "female",
            "name": {
                "title": "mrs",
                "first": "minttu",
                "last": "murto"
            },
            "email": "[email protected]",
            "picture": {
                "large": "https://randomuser.me/api/portraits/women/40.jpg",
                "medium": "https://randomuser.me/api/portraits/med/women/40.jpg",
                "thumbnail": "https://randomuser.me/api/portraits/thumb/women/40.jpg"
            }
        }
    ]
}

  Preparatory work is completed, started writing code, but before writing, but also consider the state program, in the end what information is stored, what to show on the page? Look at the operation, click the button, initiate asynchronous data request, here is an asynchronous action, asynchronous requests to start, then we should give the user prompt, require a state status, suggesting Loading, therefore, a need action; request data successfully, get to the user, in addition to status needs to be updated, it also requires a user to store the state of the captured data, a need action. If that fails, usually expressed update status failed to load, a action. As far as the state of change, both a need action, action in Redux, only to change state. Also the best plus two state, statusClass and sendingRequest. statusClass for prompt treatment of status to style, such as loading fails in red. sendingRequest its value to true or false indicates there is no request, it can be, to decide what content to display on a page, such as when loaded, no user information displayed. The two states with the change of status changes, so no action. That our state will shown below, writing in the index.js

// initial state, the state of defined morphology 
const the initialState = { 
    sendingRequest: to false , // is requesting 
    Status: '' ,     // loading tips 
    statusClass: '' ,   // load style tips 
    User: {   // User Information 
        name: '' , 
        Gender: '' , 
        In Email: '' , 
        IMG: '' 
    } 
}

  That information is then displayed on the page? tips and user load status information, status alert here with components for bootstrap, user information. Of course status plus style stautsClass with Card Components

<body style="text-align: center; margin-top: 50px">
    <button type="button" class="btn btn-primary" id="getUser">获取用户信息</button>
    <!-- 加载提示 -->
    <div style="width: 18rem; margin: 20px auto; display: none" id="status"></div>
    <!-- 用户信息 -->
    <div class="card" style="width: 20rem; margin: 20px auto; display: none" id="userInfo">
        <img class="card-img-top" src="" id="img">
        <div class="card-body">
            <h5 class="card-title" id="name"></h5>
            <h5 class="card-title" id="gender"></h5>
            <h5 class="card-title" id="email"></h5>
        </div>
    </div>

    <script src="main.js"></script>
</body>

   npx webpack-dev-server to start the server, the browser, enter localhost: 8080 look at the results, you can see in the initial state, and prompts the user to load information is display to none, does not display the entire page displays only one button. 

  So now do is when the user clicks on dynamically reveal the hint,

  And end-user information

 

  Well, now I began to write js code to implement this feature. By the time of the analysis state, an asynchronous action, click the button that sends a request, it needs to write an asynchronous action creator three synchronous action because they change status, request type:. 'USER_REQUEST'; the request succeeds type: 'USER_RECEIVED', also back with a successful user request; a data request failed type: 'USER_FAIL'; to write about these four action creator

// 三个同步action 都是返回的对象,用来改变state.
function userRequest() {  //获取数据时
    return { type: 'USER_REQUEST' }
}

function userReceived(userData) { // 获取成功
    return {
        type: 'USER_RECEIVED',
        payload: userData
    }
}

function userFail() { // 获取失败
    return {
        type: 'USER_FAIL'
    }
}

// getUser的异步action, 注意,它一定返回的是一个函数, 该函数有一个dispatch 作为参数,
// 该函数内部根据不同的情况发送不同的 同步action 来改变state
function getUser() {
    return dispatch => {
        dispatch(userRequest()); // 正在请求action,'USER_REQUEST', 整个应用的state 可以设为status为‘正在加载’

        return fetch('https://randomuser.me/api/?results=1')
            .then(
                response => {
                    if (response.ok) {
                        return response.json()
                    } else {
                        return undefined;
                    }
                },
                error => {
                    dispatch(userFail(error));  // 请求失败的action, 'USER_FAIL',status为‘加载失败’
                }
            )
            .then(json => {
                console.log(json)
                if (!json) {
                    dispatch(userFail()); 
                    return;
                }
                dispatch(userReceived(json.results)) // 请求成功的action 'USER_RECEIVED', 直接更改user
            })
    }
}

  现在有了action 就再写reducer 了,改变的状态的action只有3个,type: 'USER_REQUEST';   type: 'USER_RECEIVED',   type: 'USER_FAIL'

  type: 'USER_REQUEST', 表示正在发送请求,那么 sendingRequest 肯定设为true, status 就设为'正在加载', 正在加载 使用样式statusClass 为 'alert alert-info';

  type: 'USER_RECEIVED', 请求成功了,sendingRequest 设为false, user的信息也获取到了,status 也就不用显示了,还是初始化的'' ,statusClass 也是一样,不过user 状态的处理有点麻烦,因为在intialState中 user 是一个对象,所以在这里要重新创建一个对象user 来替换以前的user, 又因为获取回来的数据不能直接使用,所以进行拼接,然后给新创建的user 对象的属性一一进行赋值。

  type: 'USER_FAIL': 请求失败了,虽然失败了,但请求还是发送成功了, 所以sendingRequest 设为false, 由于没有获取到数据,user 信息不用更新,还是初始化的状态,但status 信息肯定要更新,status 设为 '加载数据失败' ,statusClass 为红色 'alert alert-danger'

  最后不要忘记default 分支,返回默认的初始值。

function userState(state = initialState, action) {
    switch (action.type) {
        case 'USER_REQUEST':
            return {
                ...state,
                sendingRequest: true,
                status: '正在加载',
                statusClass: 'alert alert-info'
            }
        case 'USER_RECEIVED': {
            const user = {
                name: '',
                email: '',
                gender: '',
                img: ''
            }
            user.name = `${action.payload[0].name.first} ${action.payload[0].name.last}`
            user.email = action.payload[0].email;
            user.gender = action.payload[0].gender;
            user.img = action.payload[0].picture.large;
            return {
                ...state,
                user,
                sendingRequest: false
            }
        }
        case 'USER_FAIL':
            return {
                ...state,
                sendingRequest: false,
                status: '获取数据失败',
                statusClass: 'alert alert-danger'
            }
        default:
            return state;
    }
}

  有了reducer , 终于可以创建store了,由于使用es6 模块化,所以要进行引用,由于使用了中间件redux-thunk, 所以要引入createStore, applyMiddleware,  thunk, index.js 顶部

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

  创建store

// 创建store, 要把中间件传进去
const store = createStore(userState, applyMiddleware(thunk))

  获取状态渲染页面了,使用document.getElementById 获取元素比较简单,稍微有点麻烦的是页面元素的切换,首先是 sendingRequest 的判断,为true时,正在请求,只能显示加载提示,为false的时候,表示请求完成,但又有两种情况,成功或失败,所以渲染函数如下

const statusEl = document.getElementById('status'); // 加载提示部分
const userInfoEl = document.getElementById('userInfo'); // 用户信息展示部分
const nameEl = document.getElementById('name');
const genderEl = document.getElementById('gender');
const emailEl = document.getElementById('email');
const imgEl = document.getElementById('img');

function render() {
    const state = store.getState();
    if (state.sendingRequest) { // 正在请求的时候,显示提示信息status, 用户信息不显示
        userInfoEl.style.display = 'none';
        statusEl.style.display = 'block';
        statusEl.innerHTML = state.status;
        statusEl.className = state.statusClass;
    } else { // 请求完成后它又分两种情况,
        console.log(state)
        if (state.user.name !== '') { // 请求成功,获取到user 显示user 
            userInfoEl.style.display = 'block';
            statusEl.style.display = 'none';
            nameEl.innerHTML = state.user.name;
            emailEl.innerHTML = state.user.email;
            genderEl.innerHTML = state.user.gender;
            imgEl.src = state.user.img;
        } else { // 请求失败,这里按理说应该写一个error 显示的,为了简单,直接使用了提示status 
            userInfoEl.style.display = 'none';
            statusEl.style.display = 'block';
            statusEl.innerHTML = state.status;
            statusEl.className = state.statusClass;
        }
    }
}

  使用subscribe 监听状态的变化,然后调用render 函数。

store.subscribe(() => {
    render()
})

  最后就是获取到button, 添加点击事件,发送异步action getUser

// 点击按钮发送请求
document.getElementById('getUser').addEventListener('click', () => {
    store.dispatch(getUser());
})

  整个功能就完成了。

 

Guess you like

Origin www.cnblogs.com/SamWeb/p/11332484.html