Реагистрационные крючки - Передняя и задняя обработка взаимодействия.

React.js

Во время осеннего набора автор хотел улучшить собственную боеспособность React. Недавно случайно прочитал, что Шэнь Саньюань был в «Наггетс».React Hooks и неизменяемый поток данных в действии. Итак, этим летом, слушая песни Shensanyuan Yunyinyue и используя этот буклет, я использовал react+hooks, чтобы просто имитировать приложение Geek Time.

Я думаю, что для отличного льва фронтенд-инженерии необходимо не только владеть фронтендом, но и понимать процесс бэкенда. Таким образом, в будущем при стыковке с бэкендом будет очень плавно.Здесь я рекомендую использовать mocker-api для симуляции данных, чтобы облегчить стыковку с бэкендом.

предисловие

В этом разделе необходимо выполнить следующие функции,

  1. Потяните, чтобы освежить,
  2. Потяните вверх, чтобы загрузить больше.
  3. Обработка возврата пейджинга данных.

Мы можем напрямую использовать файл better-scroll, упакованный в тернарный проект.

Во-первых, карта результатов

адрес предварительного просмотра

Часть структуры проекта

|-- pages // 页面文件夹
|   |-- forum // 讲堂页面文件夹
|   |   |-- course // 课程页面
|   |   |   |-- Forum.css
|   |   |   |-- Forum.jsx
|   |   |   |-- style.js
|   |   |   |-- store // 课程页面 store
|   |   |       |-- actionCreators.js // action 文件
|   |   |       |-- constants.js // 常量定义文件
|   |   |       |-- index.js // store 入口文件
|   |   |       |-- reducer.js // reducer 文件

Понимание структуры проекта — первый и самый важный шаг в изучении архитектуры React. В моем проекте папка pages содержит основную папку для каждой страницы. Здесь мы в основном смотрим на реализацию содержания форума, то есть лектория. Читая проект Бога Троицы, я понимаю, что можноСоздать магазин по странице, делая доступ к данным более кратким и сохраняя идеи дизайна более четкими.

интерфейсный код

Анализ страницы лекции

  • forum.jsx

Анализ мыслей: в файл forum.jsx введен инкапсулированный код Better-Scroll (вы можете напрямую использовать код Better-Scroll, инкапсулированный тройным богом) для отслеживания событий вытягивания и вытягивания страниц. Функция раскрывающегося списка, которая отслеживает действие раскрывающегося списка, может выполнять обновление раскрывающегося списка. Функция подтягивания, которая контролирует действие подтягивания, может выполнять подтягивающую нагрузку. Функция handlePullUp и функция handlePullDown передаются публичному компоненту Scroll through props.

  1. Когда браузер прослушивает соответствующее событие, такое как pulldown, он вызывает функцию handlePullDown, а функция handlePullDown вызывает деконструкцию pullDownRefresh() из реквизита Функция отправит два действия, одно — changeListOffset(0), другое это getInfoList(). changeListOffset(0) используется для установки listOffset в хранилище на 0, а getInfoList() используется для получения данных списка сведений.

код показывает, как показано ниже:

import React, { useEffect, memo } from 'react';
import { connect } from 'react-redux';
import * as actionTypes from './store/actionCreators';
import ForumList from '../../../components/ForumList/ForumList';
import Loading from '../../../common/loading/Loading';
import Scroll from '../../../common/scroll/Scroll';
import { ListContainer } from './style';
import { forceCheck } from 'react-lazyload';
import './Forum.css';
import { renderRoutes } from 'react-router-config';

function Forum(props) {
    const { route, pathList, directionList, infoList, enterLoading, pageCount, pullUpLoading, pullDownLoading } = props;
    const { getForumListDataDispatch, getInfoListDataDispatch, pullUpRefresh, pullDownRefresh } = props;
    
    const handlePullUp = () => {
        pullUpRefresh();
    };

    const handlePullDown = () => {
        pullDownRefresh();
    };

    useEffect(() => {
        // 如果没有数据 请求一次
        if (!pathList.toJS().length || !directionList.toJS().length) {
            getForumListDataDispatch();
        }
        if (!infoList.toJS().length) {
            getInfoListDataDispatch();
        }
    }, [getForumListDataDispatch, getInfoListDataDispatch, pathList, directionList, infoList])

    return (
        <div className="forum">
            <ListContainer>
                <Scroll
                    onScroll={forceCheck}
                    pullUp={handlePullUp}
                    pullDown={handlePullDown}
                    pullUpLoading={pullUpLoading}
                    pullDownLoading={pullDownLoading}
                // pulldown,监听下拉动作,可以实现下拉刷新;
                // pullup,监听上拉动作,可以实现上拉加载;
                >
                    <div>
                        <div className="forum-xw">
                            <ForumList infoList={infoList} />
                        </div>
                    </div>
                </Scroll>
            </ListContainer>
            <Loading Loading={enterLoading} title="正在加载中..." />
            {/* 重新 render routes 一次 */}
            {renderRoutes(route.routes)}
        </div>)
}

const mapStateToProps = (state) => ({
    pathList: state.forum.pathList,
    directionList: state.forum.directionList,
    infoList: state.forum.infoList,
    enterLoading: state.forum.enterLoading,
    pageCount: state.forum.pageCount,
    pullUpLoading: state.forum.pullUpLoading,
    pullDownLoading: state.forum.pullDownLoading 
})

const mapDispatchToProps = (dispatch) => {
    return {

        getForumListDataDispatch() {
            dispatch(actionTypes.getPathList())
            dispatch(actionTypes.getDirectionList())
        },

        getInfoListDataDispatch() {
            dispatch(actionTypes.getInfoList())
        },

        // 滑到最底部刷新部分的处理
        pullUpRefresh() {
            dispatch(actionTypes.changePullUpLoading(true));
            dispatch(actionTypes.refreshMoreInfoList());
        },
        
        //顶部下拉刷新
        pullDownRefresh() {
            dispatch(actionTypes.changePullDownLoading(true));
            dispatch(actionTypes.changeListOffset(0));
            dispatch(actionTypes.getInfoList());
        }
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(memo(Forum))

дизайн магазина

  • файл констант.js
export const CHANGE_INFOS = 'CHANGE_INFOS';
export const CHANGE_LIST_OFFSET = 'home/singers/CHANGE_LIST_OFFSET'; // 数据分页常量
export const CHANGE_ENTER_LOADING = 'CHANGE_ENTER_LOADING';
export const CHANGE_PULLUP_LOADING = 'home/singers/PULLUP_LOADING'; // 上拉常量
export const CHANGE_PULLDOWN_LOADING = 'home/singers/PULLDOWN_LOADING'; // 下拉常量

Файл Constants.js отвечает за определение и экспорт констант.

  • reducer.js
import * as actionTypes from './constants';

const defaultState = {
    // 一个页面只有一个 loading 值
    // 全部课程数据
    infoList:[],
    enterLoading:true,     // 加载中
    pullUpLoading: false, // 上拉加载
    pullDownLoading: false, // 下拉刷新
    listOffset: 0, // 请求列表偏移是个数
}

export default (state = defaultState, action) => {
    switch(action.type) {
        case actionTypes.CHANGE_INFOS:
            return { ...state, infoList: action.data}
        case actionTypes.CHANGE_ENTER_LOADING:
            return { ...state, enterLoading: action.data}
        case actionTypes.CHANGE_LIST_OFFSET:
            return { ...state, listOffset: action.data}
        case actionTypes.CHANGE_PULLUP_LOADING:
            return { ...state, pullUpLoading: action.data}
        case actionTypes.CHANGE_PULLDOWN_LOADING:
            return { ...state, pullDownLoading: action.data}
        default:
            return state
    }
}

Чистая функция редуктора Возвращает состояние и принимает обновления состояния. Ей соответствует только одно состояние.

  • actionCreators.js
// 负责进行数据请求
import * as actionTypes from './constants';
// 请求接口文件
import { getInfoListRequest} from '../../../../api/request';

export const changeListOffset = (data) => ({
    type: actionTypes.CHANGE_LIST_OFFSET,
    data
  });

// 进场 loading
export const changeEnterLoading = (data) => ({
    type: actionTypes.CHANGE_ENTER_LOADING,
    data
})

//滑动最底部loading
export const changePullUpLoading = (data) => ({
    type: actionTypes.CHANGE_PULLUP_LOADING,
    data
  });
  
//顶部下拉刷新loading
export const changePullDownLoading = (data) => ({
    type: actionTypes.CHANGE_PULLDOWN_LOADING,
    data
  });

export const changeInfoList = (data) => ({
    type: actionTypes.CHANGE_INFOS,
    data
})

// 获取详情列表数据
export const getInfoList = () => {
    return ( dispatch, getState) => {
        // 获取原store中的偏移量
        const offset = getState().forum.listOffset;

        // 偏移量传进接口请求中
        getInfoListRequest(offset).then(res=> {
            const data = res.infos
            dispatch(changeInfoList(data));
            // 拿到数据  EnterLoading 变成false
            dispatch(changeEnterLoading(false));
            dispatch(changePullDownLoading(false));
            dispatch(changeListOffset(data.length));
        })
        // 如果拿到错误的数据
        .catch(() => {
            console.log('详情列表数据传输错误')
        })
    }
}

// 加载更多数据
export const refreshMoreInfoList = () => {
    return ( dispatch, getState ) => {
     // 获取 原来的 listOffset 和 infoList 数据
        const offset = getState().forum.listOffset;
        const infos = getState().forum.infoList;
        getInfoListRequest(offset).then(res => {
            // 让当前的 infos 和新请求的 infos 使用拓展运算符展开并拼接成一个新的数据;
            const data = [...infos, ...res.infos]
            dispatch(changeInfoList(data));
            dispatch(changePullUpLoading(false));
            dispatch(changeListOffset(data.length));
        })
        .catch(() => {
            console.log('详情列表数据传输错误')
        })
    }
}

Анализ мыслей:

При получении данных списка сведений, то есть вызове getInfoList, нам нужно сначала получить смещение в исходном хранилище и отправить запрос.После успешного получения данных мы устанавливаем обновление раскрывающегося списка в false и устанавливаем смещение списка. получить длину данных.

Когда нам нужно загрузить больше данных, нам нужно использовать GUTSTATE, чтобы сначала получить оригинальные компенсировать ListOffset и Infolist Data. После получения данных мы срачаем старые данные и новые данные, чтобы получить новые данные, а затем изменить данные магазина, чтобы наши предыдущие данные не будут потеряны. И установите ListOffset в длину новых данных, чтобы убедиться, что Paginate данных является правильным. Мы приехали в GetInfolist внутри магазина

export const getInfoList = () => {
    return ( dispatch, getState) => {
        // 获取原store中的偏移量
        const offset = getState().forum.listOffset;

        // 偏移量传进接口请求中
        getInfoListRequest(offset).then(res=> {
            const data = res.infos
            dispatch(changeInfoList(data));
            // 拿到数据  EnterLoading 变成false
            dispatch(changeEnterLoading(false));
            dispatch(changePullDownLoading(false));
            dispatch(changeListOffset(data.length));
        })
        // 如果拿到错误的数据
        .catch(() => {
            console.log('详情列表数据传输错误')
        })
    }
}

Эта функция возвращает функцию, которая принимает два параметра: dispatch и getState, dispatch используется для изменения содержимого в хранилище, getState используется для получения содержимого в хранилище, мы сначала получаем listOffset из хранилища, а затем передаем смещение как параметр для getInfoListRequest , функция получит результат, а затем изменит данные в хранилище и изменит EnterLoading на false, PullDownLoading на false и изменит listoffset в хранилище на длину полученных в данный момент данных.

файл интерфейса запроса

  • config.js
import axios from 'axios';
// 推荐使用 axios 兼容性更好

export const baseUrl = "http://localhost/data"; // 全局的后端 api 前缀

const axiosInstance = axios.create({
    baseURL:baseUrl
})

// 回复处理
axiosInstance.interceptors.response.use(
    res => res.data,
    err => {
        console.log(err, '网络错误')
    }
)

export { axiosInstance }
  • request.js
// 获取学习路径和课程方向的数据

import { axiosInstance } from './config';

// 获取全部课程的数据
export const getInfoListRequest = count => {
    return axiosInstance.get(`/infos?offset=${count}`);
}

внутренний код

const infos = require('./data/infos.json');

module.exports = {
    // 分页功能数据
    'GET /data/infos': (req, res) => {
        // 获取传进来的 参数offset
        const { offset = 0 } = req.query;
        // 使用 Number 转化为 Number 型
        const data = infos.slice(Number(offset) , Number(offset)  + 10);
        res.json({
            "infos":data});
    }
}

Пользователь сдвигает экран, чтобы вызвать соответствующее событие.Входящее смещение может быть деконструировано через req.query.Если оно не передано, значение по умолчанию равно 0. Мы можем разрезать массив в соответствии с этим смещением. Затем верните соответствующие данные.

Входной файл index.js

const express = require('express');
const path = require('path');
const apiMocker = require('mocker-api');

const app = express();

apiMocker(app, path.resolve('./mocker/mocker.js'))
app.listen(8080);

Вышеизложенное является основным содержанием этого раздела.Личные навыки не являются хорошими, и проект все еще имеет недостатки.Вы можете исправить меня.Я улучшу его.Ниже приведен исходный код проекта.Исходный код проекта Github React-time geek-time.

Автор готовится к осеннему набору, приглашаю всех к обсуждению и совместной работе!