Во время осеннего набора автор хотел улучшить собственную боеспособность React. Недавно случайно прочитал, что Шэнь Саньюань был в «Наггетс».React Hooks и неизменяемый поток данных в действии. Итак, этим летом, слушая песни Shensanyuan Yunyinyue и используя этот буклет, я использовал react+hooks, чтобы просто имитировать приложение Geek Time.
Я думаю, что для отличного льва фронтенд-инженерии необходимо не только владеть фронтендом, но и понимать процесс бэкенда. Таким образом, в будущем при стыковке с бэкендом будет очень плавно.Здесь я рекомендую использовать mocker-api для симуляции данных, чтобы облегчить стыковку с бэкендом.
предисловие
В этом разделе необходимо выполнить следующие функции,
- Потяните, чтобы освежить,
- Потяните вверх, чтобы загрузить больше.
- Обработка возврата пейджинга данных.
Мы можем напрямую использовать файл 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.
- Когда браузер прослушивает соответствующее событие, такое как 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.
Автор готовится к осеннему набору, приглашаю всех к обсуждению и совместной работе!