我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!
一、前言
继数周前通过React完成了 OPPO商城的部分界面后,随着近几周学习的深入,接触到了
redux
,在学习后知道了其对于数据管理方面的强大之处后,决定将之前写的 OPPO 商城进行升级,于是就有了这一版React + redux
的 OPPO 商城2.0版本
接下来就通过以实战 OPPO 商城的方式一起走入
React + redux
的世界
二、redux
1. 何为redux
- Store:Store 就是保存数据的地方,你可以把它看成一个容器。
- State:Store对象包含所有数据。
- Action:在原来的useState中,State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。
- ActionCreators:指明Action变化的种类
- dispatch() :是 View 发出 Action 的唯一方法。
- Reducer:Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
就假如这个组件是一场战争,store就是一个司令,它呢就负责统筹全局;Action Creators 就是通讯员将战场信息(state)通过发报员dispatch发送给参谋长Reducers来处理;之后将新的信息(newState)报告给司令Store在由司令来排兵布阵。
就本组件而言,其数据量并不大,所以并不能够体现出redux
由多么强大和方便。但是为了方便以后为大型项目开发做准备,也同样为了方便理解redux这里还是统一用了redux来管理数据。也一改之前项目在组件中用useState来引入数据;它虽然剥夺了页面管理数据的能力,但是通过一个Store来管理数据更加便于我们多人协作,以及数据的后期管理。
三、组件详情
1、组件展示
左边为React开发OPPO商城,右边为原始商城页面
2、组件更新
本项目基 OPPO商城1.0(1:1 纯手工打造:OPPO 商城 - 超详细小白React入门项目讲解 - 掘金 (juejin.cn)) 的基础上做的更新有:
- 将本项目全部改用
redux
进行数据管理 - 进行了接口请求的封装
- 增加了calssify组件页面
四、组件实现
1. 组件解构与组件树
组件与组件树与上一个版本并没有多大的不同,只是在需要运用redux
管理数据的组件页面多了一个Store文件
2、封装网络请求
这个版本的api网络请求文件夹我分为了两个文件来封装,避免了request文件过于复杂和重复,也为后期管理和修改请求文件提供便利;害在config文件中设置了一个拦截器来确保正确性,一旦输入错误会抛出我们写的err。
3、redux
1、总仓的创建
1、先用 Provider 包裹,相当于给 App 根组件提供仓库服务
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
2、 创建总仓库Store
import { createStore, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer,
composeEnhancers(
applyMiddleware(thunk)
)
)
export default store;
- applyMiddleware :提供中间件 thunk(除此之外还有 logger 等)
- composeEnhancers :将多个中间件合并在一起,方便调试。我用的是
Redux DevTools
3、 总Reducer 文件 用来集合所有子仓的reducer
import { combineReducers } from 'redux'
import { reducer as classifyReducer } from '../pages/Classify/store/index'
import { reducer as homeReducer } from '../pages/Home/store/index'
export default combineReducers({
classify: classifyReducer,
home: homeReducer
})
2、 子仓从创建
1、 子仓的解构
位置:在每一个需要请求数据的组件中创建
index.js文件,将子仓的数据交给总仓
import reducer from "./reducer";
import * as actionCreators from './actionCreators'
export {
reducer,
actionCreators
}
actionCreators.js(通讯员)文件 将action(信息)用dispatch(发报员)方法交给reducer(参谋长)
import * as actionType from './constants'
import { getClassifyListRequest } from '@/api/request'
export const changeClassifyList = (data) => ({
type: actionType.CHANGE_CLASSIFY_LIST,
data: data
})
export const getClassifyList = () => {
return (dispatch) => {
getClassifyListRequest().then(data => {
console.log(data,'-------------------')
const action = changeClassifyList(data.classifylist)
dispatch(action)
console.log(action,'----')
})
}
}
reducer.js(参谋长) 文件
存放、修改状态\
注意:返回的新状态,与旧状态引用地址不同,为了方便溯源,也可以用 Object.assign() 方法,效果一样
import * as actionTypes from './constants'
const defaultState = {
classifyList: []
}
export default (state = defaultState, action) => {
// 通过 type 判断修改哪个数据
switch(action.type) {
case actionTypes.CHANGE_CLASSIFY_LIST:
// 返回了新的状态
return {
...state,
classifyList: action.data
}
default:
return state;
}
}
constants.js 文件,存放 type 类型配置
export const CHANGE_CLASSIFY_LIST = 'CHANGE_CLASSIFY_LIST'
至此store文件就创建完成了,可以去Redux DevTools
查看数据
3、组件连接仓库
4、classify组件的实现
1. 设置路由懒加载
import { useState, lazy } from 'react'
import { Routes, Route, Link} from 'react-router-dom'
import Home from '../pages/Home'
const Classify = lazy(() => import('../pages/Classify'))
const Shopcar = lazy(() => import('../pages/Shopcar'))
const Mine = lazy(() => import('../pages/Mine'))
const Search = lazy(() => import('../pages/GoSearch'))
const RoutesConfig = () => {
return (
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/home" element={<Home />}></Route>
<Route path="/classify" element={<Classify />}></Route>
<Route path="/shopcar" element={<Shopcar />}></Route>
<Route path="/mine" element={<Mine />}></Route>
<Route path="/search" element={<Search />}></Route>
</Routes>
)
}
export default RoutesConfig
2. 组件功能实现
由功能可知这个组件主要就是要实现一个双联动效果
组件展示:
部分代码:
1、实现双联动效果
// 实现双联动效果
const scrollToAnchor = (anchorName) => {
if (anchorName) {
let anchorElement = document.getElementById(anchorName)
if (anchorElement) {
anchorElement.scrollIntoView({
block: 'start',
behavior: 'smooth'
})
}
}
}
2、 左边栏
// 左边栏
const renderInfo = () => {
return classifyList.map(item => (
<div className="left-data" key={item.id}>
<a onClick={() => scrollToAnchor(item.id)}>
<span>{item.title}</span>
</a>
</div>
))
}
3、 右边栏
// 右边栏
const renderSaleSlide = () => {
return classifyList.map(item => {
return (
<div className="menu-box-detail" key={item.id}>
<div className="menu-top">
<div className="title-img-box">
<img className="title-img-box_img" src={item.img} />
</div>
<div className="top-title" id={item.id}>
{item.name}
</div>
<span>{item.description}</span>
</div>
<div className="menu-box">
{ item.phone && item.phone.map(element => {
return (
<><div className="top-title" key={element.title_id}>
<span>{element.phone_title}</span>
</div><div key={element.id} className="menu-detail">
<div className="menu-detail-box">
<div className="menu-item" key={element.id}>
<div className="img-bubble">
<div className="img-box">
{/* <LazyLoad
placeholder={
<img width="100%" height="100%"
src={loading}
/>}> */}
<img className="sale-img" src={element.img} alt="" />
{/* </LazyLoad> */}
</div>
</div>
<section>
<p className="fooddetail-info">
<span>{element.name}</span>
</p>
<p className="fooddetail-sale">
<span>{element.introduce}</span>
</p>
<div className="fooddetails-space"></div>
<span className="sale_price">
<span>{element.price}</span>
</span>
</section>
</div>
</div>
</div></>
);
})}
</div>
</div>
);
});
};
4、 组件调用方法
return (
<Wrapper>
<Search/>
<div className="sale-box">
<div className="sale-main">
<div className="sale-left">
<ul>
{renderInfo()}
</ul>
</div>
<div className="sale-detail-box">
<div className="sale-detail">
{renderSaleSlide()}
</div>
</div>
</div>
</div>
</Wrapper>
)
配合组件连接Store仓库的方法就可以拿到数据就可以实现出双联动效果
这个组件可以进行封装以后复用
五、结语
这次项目的完成过程中,对于运用redux管理数据还是比较麻烦的,我在拿数据过程中大毛病没有,小毛病还是不断的,但是redux的编写还是有迹可循的,对着模板还是比较简单,配合多次练习越来越熟悉就好。
至此这个项目就结束了,虽然只实现了页面组件,交互方面还很粗糙,我会随着学习的越来越深入慢慢来丰富这个组件,争取在不久之后真的做到1:1复刻,感谢各位阅读了。最后麻烦大家动一动发财的小手戳一个小星星哦。
github [源码地址](wenhaolan1/OPPO-shop (github.com))