Teach you how to do it by hand: Kuaishou's latest popular "One Sweet Camera" - Redux Upgraded Version (2)

foreword

React component development has also been studied for a while. An article published on Nuggets some time ago: Teach you how to do it: Kuaishou's latest popular "One Sweet Camera" - a must-have project for novice react development is my react development A water test chapter of the project The
template page of a sweet camera is still in its infancy. Now I combine the redux I have learned during this time, and hand over the data in the project to redux management, optimize the page, and add some new functions. This article will be optimized around the previous article. If you don't understand, you can move to the previous article~

On-page optimization section

When this project started, my learning was not enough, and there are still many places that are relatively rough. The optimization part of this article will be from page adaptation, routing, page and other aspects.

optimization project

  • Adaptive page : When the project was developed last time, the unit used was px, and the mobile effect on the computer showed everything was normal, but when it was used gitPageon the mobile phone, the layout was a bit confusing, and the page adaptive was added to solve this problem.
  • Lazy Loading of Routes : Loading on Demand
  • Navigation bar optimization : The previous page navigation bar switching is tab switching, which is too cumbersome to write. The use of secondary routing can reduce the cumbersome code
  • Redux data management : Most of the data status in the project is changed to reduxtake over, which is convenient for management
  • Search bar jump page optimization : cut-in and cut-out animation effect
  • Implementation of lazy loading of pictures : After learning the cloud music project of Shensanyuan, the lazy loading of pictures has been put on the agenda. When the picture has not been loaded successfully, it is replaced by a default picture, which can improve the user experience.

Responsive page

移动端的适应性与电脑端不同,使用px在电脑端布局看着是没有问题,但一但到了真正的移动端,由于手机型号的多变,页面局部也需要进行调整,px是绝对单位,无法根据页面大小进行相应调整,为了优化用户体验感,我采用rem来进行自适应布局。
在项目目录下新增一个public文件夹,里面js文件中的adapter.js来写页面自适应布局

rem :是一个相对单位,是指相对于根元素的字体大小的单位。这样就意味着,我们只需要在根元素确定一个px字号,则可以来算出元素的宽高。对于页面自适应来说,rem这个相对单位有很大的作用

页面自适应代码如下:这是一个通用样式,html 的font-size 被设为了16px,经过尝试,这个大小可以刚好使页面修改量为最少(单指我这个项目)。

var init = function () {
    var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
    if (clientWidth >= 640) {
      clientWidth = 640;
    }
    //设计稿为750px ,css则需/2,为375px
    var fontSize = 16 / 375 * clientWidth;
    document.documentElement.style.fontSize = fontSize + "px";
  }
  
  init();
  
  window.addEventListener("resize", init);
  
  //如果页面宽度超过640px,那么页面中的宽度恒为640px

  //否则页面中html 的font-size的大小为 16 *(当前宽度/375)

路由的懒加载

缺点: 上篇文章中与路由相关的组件都是直接导入的,整个网页打开时就默认加载所有网页,这样会拖延首屏加载时间,导致用户体验感不强。路由懒加载就是来解决这个问题的!

优化: 新增的路由懒加载lazy,顾名思义,就是只加载你当前点击的那个模块。按需去加载路由对应的资源,提高首屏加载速度
(tip:首页不用设置懒加载,而且一个页面加载过后再次访问不会重复加载)

import { useState, lazy} from 'react'
import { Routes, Route, Link,Navigate } from 'react-router-dom'  

import Tem from '../pages/Tem' //首屏不需要懒加载
const Vedio = lazy(() => import('../pages/Vedio'))
const Pic = lazy(() => import('../pages/Pic'))
const Kd = lazy(() => import('../pages/Kd'))
const Searchk = lazy(() => import('../pages/Searchk'))
const Tpmb = lazy(() => import('../pages/Tem/Tpmb'))
const Spmb= lazy(() => import('../pages/Tem/Spmb'))
const Login = lazy(() => import('../components/Login'))
const Shop = lazy(() => import('../components/Shop'))  
const Geren = lazy(() => import('../components/Geren'))
const Tpmbdetail = lazy(() => import('../pages/Tem/Tpmbdetail'))
const Spmbdetail = lazy(() => import('@/pages/Tem/Spmbdetail'))

const RouteConfig =() =>{
    return (
        <Routes>
        <Route path="/" element={<Navigate to ='/temp'/>} />
        <Route path="/temp" element={<Tem/>}>
           {/* 二级路由  */}   
            <Route path="/temp/tpmb" element={<Tpmb/>}></Route>
            <Route path="/temp/spmb" element={<Spmb/>}></Route>
        </Route>
        <Route path="/pz" element={<Pic/>}></Route>
        <Route path="/vedio" element={<Vedio/>}></Route>
        <Route path="/kd" element={<Kd/>}></Route>
        <Route path="/select" element={<Searchk/>} />
        <Route path="/login" element={<Login/>} />
        <Route path="/geren" element={<Geren/>} />
        <Route path="/shop" element={<Shop/>} />
        <Route path="/tpmbdetail/:id" element={<Tpmbdetail/>} />
        <Route path="/spmbdetail/:id" element={<Spmbdetail/>} />
      </Routes>
    )
}

export default RouteConfig

导航栏优化

1. 第一层导航栏:二级路由

优化: 双层Tab键切换还是显得有些繁琐,于是第一层导航栏则换成了页面的二级路由,整个页面由路由包裹着,使用路由进行页面跳转十分方便快捷
操作: 在模板页面中用<TpNav/> 占位,将需要用到的二级路由写进一个数组,再用map方法和Swiper将二级路由展现出来,二级路由下的页面则由 <Outlet/> 输出

代码仅展示部分:
增加二级路由

        <Route path="/temp" element={<Tem/>}>
           {/* 二级路由  */}   
            <Route path="/temp/tpmb" element={<Tpmb/>}></Route>
            <Route path="/temp/spmb" element={<Spmb/>}></Route>
        </Route>

实现二级路由

// 将二级路由写入数组中
 let TpNavs = [
        { id: 1, desc: '图片模板', path: 'tpmb'},
        { id: 2, desc: '视频模板', path: 'spmb'}
    ]
 // 使用map 和 Swiper 将二级路由展现出来
  <div className="navbar swiper-container">
                <div className="nav-box swiper-wrapper">
                {
                    TpNavs.map((item, index) => {
                        return (
                            <NavLink
                                index={index}
                                to={`/temp/${item.path}`}
                                key={item.id}
                                className="nav-item swiper-slide"
                            >
                            {item.desc}
                            </NavLink>
                        )
                    })
                }
                </div>
            </div>

2. 第二层导航:超长自动滑动导航切换

上次项目中导航栏的滑动主要是由css 样式实现的,且无法进行自动滑动,滑动过程中还有滑动条出现,体验感还有待加强。
优化:使用anted-mobile 组件库中 CapsuleTabs 组件,实现超长自动滑动,具体实现还需修改

ps:antd-mobile是由蚂蚁金融团队推出的一个开源的react组件库,相当于Weui 组件库,能提供许多便捷功能的组件,打造react项目方便快捷当属它!大家开发项目的时候可以去找找有没有便捷的组件~

要在 CapsuleTabs组件中实现tab键的切换,需要另外设置一个类名。用classnames设置active时的tab键值,就能实现tab键的切换了

注意事项--修改antd-mobile样式

antd-mobile样式有点难改,经过我多次尝试和查询网络,现在最有效的方法是使用antd-mobile中的组件都需要另建一个css文件,在页面源码中找到需要修改元素的类名,所有包含在组件中的样式都需要在css文件中写样式

<CapsuleTabs  defaultActiveKey='2'>
          <CapsuleTabs.Tab className={classnames({active:tab1 == "hot"},'cap')} title={<a   onClick={()=> changeTab1("hot")}>热门</a>} key='1'>
          </CapsuleTabs.Tab>
          <CapsuleTabs.Tab className={classnames({active:tab1 == "new"})} title={<a onClick={()=>changeTab1("new")}>最新</a>} key='2'>
          </CapsuleTabs.Tab>
          <CapsuleTabs.Tab className={classnames({active:tab1 == "biye"})} title={<a  onClick={()=>changeTab1("biye")}>毕业季</a>} key='3'>
          </CapsuleTabs.Tab>
          <CapsuleTabs.Tab  className={classnames({active:tab1 == "summy"})} title={<a onClick={()=>changeTab1("summy")}>夏日</a>} key='4'>
          </CapsuleTabs.Tab>
          <CapsuleTabs.Tab className={classnames({active:tab1 == "kuai"})} title={<a  onClick={()=>changeTab1("kuai")}>快手爆款</a>} key='5'>

          </CapsuleTabs.Tab>
          <CapsuleTabs.Tab className={classnames({active:tab1 == "hbizhi"})} title={<a  onClick={()=>changeTab1("bizhi")}>壁纸</a>} key='6'>

          </CapsuleTabs.Tab>
          <CapsuleTabs.Tab className={classnames({active:tab1 == "pai"},'cap')} title={<a  onClick={()=>changeTab1("pai")}>拍立得</a>} key='7'>

          </CapsuleTabs.Tab>
        </CapsuleTabs>

修改样式后实现效果

chrome-capture-2022-6-13 (1).gif

redux数据管理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。
随着项目页面的增加,状态变得越来越复杂,

状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,页面的变换也有可能会引起状态的变化;
当应用程序复杂时,state在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常难以控制和追踪 ,而redux可以解决这个问题——redux能够 集中式管理(读/写) react 应用中多个组件共享状态

Redux 可以用这三个基本原则来描述:
单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。 State 是只读的
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
使用纯函数来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducers

  • store

store 就是保存数据的地方,它相当是一个容器。整个应用只能有一个 store。store 就是将 state、action 与 reducer 联系在一起的一个对象,在这个项目中,在一个总store 下还有两个子store掌管着数据状态

  1. 创建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   
  • redux-thunk 是一个比较流行的 redux 异步 action 中间件,比如 action 中有通过 axios通用远程 API 这些场景,那么就应该使用 redux-thunk 了。redux-thunk 帮助你统一了异步和同步 action 的调用方式,把异步过程放在 action 级别解决,对 component 没有影响。

2.汇总reducer
这个项目中有两个页面需要进行数据渲染,为了方便管理,我分别创建了两个子store,在reducer文件中将两个子store的reducer引入合并成总的reduce文件

import { combineReducers } from 'redux'   
import { reducer as TemReducer} from '@/pages/Tem/store/index'
import { reducer as GerenReducer } from '@/components/Geren/store/index'

export default combineReducers({
    Tem:TemReducer,
    Geren:GerenReducer
})
  • actionCreators

所有的数据都需要通过派发(dispatch)action 来更新,action 也是一个普通的 js 对象,这个对象包含两部分:更新的 typedata ,拉取数据需要在获取数据后dispatch (action)
仅展示部分代码:

export const changeBannerList = (data) =>({
    type:actionTypes.CHANGE_BANNER,
    data
})

export const getBannerList = () =>{
    return (dispatch) => {
        getBanners()
        // console.log(data)
            .then(data => {
                const action = changeBannerList(data)
                dispatch(action)
                dispatch(changeEnterLoading(false))
        })
    }
}

  • reducer

将 state 和 action 联系在一起,也就是根据旧的 state 和 action, 产生新的state 的纯函数

const reducer= (state = defaultState,action)=>{
    switch (action.type){
        case actionTypes.CHANGE_BANNER:
            return  {
                ...state,
                bannerList:action.data
            }
      ...
            default:
                return state
    }
    
}
  • constants

本文件用于管理所有actiontype,方便维护

export const CHANGE_BANNER = 'CHANGE_BANNER'
export const CHANGE_TPMB_LIST = 'CHANGE_TPMB_LIST'  
export const CHANGE_SPMB_LIST = 'CHANGE_SPMB_LIST'  
export const CHANGE_ENTERLOADING = 'CHANGE_ENTERLOADING'
export const CHANGE_ALBUM = 'CHANGE_ALBUM'
export const CHANGE_TPMBLIST_ID = 'CHANGE_TPMBLIST_ID'
export const CHANGE_TPMB_STAR = 'CHANGE_TPMB_STAR'
export const CHANGE_SPMB_STAR = 'CHANGE_SPMB_STAR'

搜索栏跳转页面优化

通过学习神三元的云音乐项目,我get到了搜索栏切入切出的动画效果,于是,我将它运用到了我的搜索栏中,优化页面跳转

具体实现效果如下:
操作: 点击搜索栏,搜索页面会从右侧平移过来,点击取消,动画消失

24.gif 代码实现:
安装依赖-- React过渡动画

在开发中,我们想要给一个组件的显示和消失添加某种过渡动画,可以很好的增加用户体验。 React 可以为我们提供react-transition-group

这个库可以帮助我们方便的实现组件的 入场 和 离场 动画,使用时需要进行额外的安装:

npm i react-transition-group

引入CSSTransition--- 在前端开发中,通常使用CSSTransition来完成过渡动画效果

import { CSSTransition } from 'react-transition-group'

<CSSTransition   
        in={show} //用于判断是否出现的状态
        timeout={300} // 动画持续时间
        appear={true}   
        classNames="fly"  // classNames值,防止重复
        unmountOnExit //元素退场时,自动将DOM删除
        onExited={() =>{
          navigate(-1)
        }}
    >
    ...搜索页面内容
    <CSSTransition />

CSSTransition的动画效果主要由css样式来实现

  • 它们有三种状态,需要定义对应的CSS样式:
  • 第一类,开始状态:对于的类是-appear、-enter、exit;
  • 第二类:执行动画:对应的类是-appear-active、-enter-active、-exit-active;
  • 第三类:执行结束:对应的类是-appear-done、-enter-done、-exit-done;
//搜索页面实现动画效果的css
export const Container = styled.div`
    position: fixed;
    top: 0;   
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    z-index:100;
    overflow: hidden;
    background: #f2f3f4;
    transform-origin:right bottom;  
    &.fly-enter,&.fly-appear {
        opacity:0;
        transform:translate3d(100%,0,0) ;
    }
    &.fly-enter-active,&.fly-apply-active {
        opacity:1;
        transition:all .3s; 
        transform: translate3d(0,0,0);
    }
    &.fly-exit {
        opacity: 1;
        transform: translate3d(0,0,0);
    }
    &.fly-exit-active {
            opacity: 0;
            transition: all .3s;
            transform: translate3d(100%,0,0);
        }
`

图片懒加载的实现

在学习了神三元的云音乐项目后,我又get到了一项新技能——图片的懒加载 ,利用Scroll组件和react-lazyload 中的Lazyload 组件实现图片下滑后的懒加载

  • onScroll 事件:在页面滚动时触发此事件,forceCheck 是从react-Lazyload中解构出来的函数,实现图片移动到视口时进行加载,<Lazyload/> 组件为数据提供了一个默认图片,还未到达视口的图片由默认图片替代,等图片滚动到视口时再进行加载,优化了用户体验感。
// Scroll 
<Scroll onScroll ={forceCheck}>
     <TpmbList  tpmbda={tpmbda}/> 
     </Scroll>
//Lazyload 
<ListWrapper>
     {tpmbda.map(item => 
    <Link to ={`/tpmbdetail/${item.id}`} key = {item.id}>
      <List key = {item.id}>
        <div className="img_wrapper">
        <Lazyload placeholder={<img 
        width="100%" height="100%"
        src={tp}/>
      }>
       <img src={item.img} alt="" />
        </Lazyload>
        </div>
       
        
        <p className="get" onClick={() => {
              Toast.show({
                icon: 'loading',
                content: '加载中…',
              })
            }}>Get 同款</p>
      <p className='title'>{item.title}</p>
     </List></Link>)}
    </ListWrapper>

具体实现效果为:

chrome-capture-2022-6-14 (2).gif

页面新增功能

  • 简易版登录功能:实现登录页面跳转用户页面
  • 图片导航实现相册选取
  • 图片细节页面的实现:模板的下滑,用户的关注收藏
  • 视频模板细节页面的实现:视频的自动播放,收藏数等

简易版登录功能

实现效果:

7.gif

由于没有后端,登录功能只能做一个简易版的,主要是使用变量和localstorage实现

  1. 登录功能,在默认头像上添加点击事件,引入antd-mobile中的Mask组件,制作登录弹窗,通过visible变量来控制弹窗的出现和消失
{/* 遮罩层 */}
             <Mask visible={visible}  >
             <div className="content">  
                     <p>登录一甜相机</p>                        
                      <p> get专属于你的美颜相机!</p>
              <div className='btns'>
                <span onClick={() => setVisible(false)}>取消</span>
                <span className='login' onClick={() => (setShowlogo(false))}><Link to="/login" > 登录 </Link></span>
              </div>
                 </div>
              </Mask>   
  1. 点击登录则跳转登录页面,下面的复选框check值由变量控制,在登录按钮中添加点击事件,通过判断check的值来判断页面是否能成功登录,check为false则跳出弹窗提示进行勾选,若为true则跳转页面
//判断复选框是否被勾选
  const setSelect = () =>{
    if(check){
      navigate('/temp/tpmb')
    }else{
      Modal.alert({
        content: '请勾选同意后再进行登录'
      })
    }
  }
  1. 实现默认头像图片和用户头像转换:在登录页面中添加一个localstorage变量

window.localStorage.showuser = 'showuser'

点击登录,在页面跳转成功的页面上添加一个判断函数 :

if(!window.localStorage.getItem('showuser')){

window.localStorage.setItem('showuser','');
}else{}

 { showuser && user.map(item => {
           return (
          <span className="im" key={item.id}><Link to="/geren"><img src={item.img}/></Link></span>  
      )
    }) }

通过判断showuser的值来实现用户头像的替换,由于localstorage存储的值均为string类型,要使showuser展示出boolean类型,为false则设为空值,即解决了头像无法转换的问题

图片导航实现相册选取

和上面登录功能一样,照片拉取也是关于后端的功能,于是我就做了一个双层弹窗转换相册的功能

具体实现效果:

8(1).gif

图片导航采取的是双层嵌套弹出层的组件,也是在antd-mobile组件库中引入的

双层弹出层

<Popup
              visible={visible7}
              showCloseButton
              onClose={() => {
                setVisible7(false)
              }}
              bodyStyle={{
                borderTopLeftRadius: '1px',
                borderTopRightRadius: '1px',
                minHeight: '100vh',
              }}
            >
               
              {mockContent}
            </Popup>
              </li>
              <Popup
          visible={visible}
          bodyStyle={{ height: '90vh' }}
        >
          <div style={{ padding: '24px' }} className="album" >
            {renderAlbums()}
          </div>
        </Popup>

相册选取功能

使用函数将相册列表渲染上去,点击列表选择相册则转至指定相册
<span>{albumName?albumName:''}</span>

  const renderAlbums = () => {
    return albumList.map(({id,nm,img}) => {
        return <Link 
        className="album_name"
        to={{
          search:`name= ${nm}`  // ? 后面的参数  0
        }}
        onClick={() => {
          setVisible(false)
        }}
        key= {id}>
         <img src={img} /> 
            {nm}
        </Link>
    })
  }

图片细节页面实现

这个功能实现的是由首页的图片模板点进去的细节页面,这个页面分为三个步骤去实现:

  1. 使用useParams 获取这个页面图片的id值,使用slice() 这个api将图片列表数据从此id开始截取 ,并使用map函数将图片模板渲染至页面上

const { id } = useParams() //获取页面id值
setData1(tpmbList.slice(id-1)) // id从0开始,即id-1为本页面id值

 const renderTpmbdetail =() => {
    return data1.map(item =>{
      return (
        <List key = {item.id}>
             <NavBar>
          <CloseOutline   onClick={() => navigate(-1)} className="close"/>
              {item.artist}<span className='gz' onClick={() =>setAttention(item.id)}>{ (item.attention)?'已关注':'关注'}</span>
             {/* { console.log(item.attention)} */}
              <SendOutline className='share'  onClick={() => {
                setVisible5(true)
              }}/>
            </NavBar>
            <div className='dw'>
            <img src={item.img} alt="" />
          <p>
            <span className='title'>{item.title}</span>
            <span className='sc'>收藏{item.star1 ? item.star : item.star-1}</span>
          </p>
            </div>
            <div className='caozuo'>
              <span className='star'><Rate className='star_a' count ={1} 
              style={{
                '--active-color': '#ea84ae',
                '--inactive-color':'#fec7df'
              }}
              onChange={() =>setStar(item.id)}
              /></span>
              <span className='zi'>Get同款</span>
            </div>
        </List>
      )
    })
  }

具体效果为:

9.gif
2. 实现关注作者功能 :关注功能实现要将id传进数据列表中,将attention 的值改为相反值,再将数据重传,重新渲染

  • 在函数中触发点击事件,将id值作为参数传入 ,在函数中运行dispatch函数后再判断
{item.artist}<span className='gz' onClick={() =>setAttention(item.id)}>{ (item.attention)?'已关注':'关注'}</span>

const setAttention = (id) =>{
  changeTpmbListByIdDispatch(id)
  setData1(tpmbList.slice(id-1))
}
  • 在页面上dispatch一下修改关注对象,将id值作为data传入
const mapDispatchToProps =( dispatch ) =>{
  return {
      changeTpmbListByIdDispatch(data) {
        dispatch(actionCreators.changeTpmbListById(data))
      }
  }
}
  • reducer里面进行数据修改
仅展示部分代码:
// 根据id修改attention  
const changeTpmbById = (list,id) =>{
  console.log(list)
  console.log(id)
  let index = list.findIndex(data => id == data.id);
  list[index].attention = !list[index].attention;
  return list;
}
// 根据action的值返回数据
case actionTypes.CHANGE_TPMBLIST_ID:
          // console.log(action.data)  通过id改变attention状态  
               return {
                ...state,
                tpmbList:changeTpmbById(Object.assign([],state.tpmbList),action.data)
               }

具体实现:

10(2).gif

3.实现收藏功能: 与关注相仿,收藏功能也需要将id传入数据列表中进行查找到相应的了列表项,将star1的值改为true,再重新渲染数据

  • 在Rate中触发onChange事件,将id值作为参数传入,在函数中运行dispatch函数
const setStar = (id) => {
  changeTpmbListStar(id)
  // changetStarDispatch(id)
  setData1(tpmbList.slice(id-1))
}
//onChange事件  
<Rate className='star_a' count ={1} 
              style={{
                '--active-color': '#ea84ae',
                '--inactive-color':'#fec7df'
              }}
              onChange={() =>setStar(item.id)}
              /></span>
  • mapDispatchToProps Create a dispatchfunction with parameters in
const mapDispatchToProps =( dispatch ) =>{
  return {
     
      changeTpmbListStar(data){
        dispatch(actionCreators.changeTpmbListStar(data))
      }
  }
  • reducerQuery and modify data in
// 修改函数   
const changeTpmbStar =(list,id) => {
  let ind = list.findIndex(data => id == data.id);
  list[ind].star1 = !list[ind].star1;
  return list
}
//根据action的值调用修改函数
   case actionTypes.CHANGE_TPMB_STAR:
               return {
                ...state,
                tpmbList:changeTpmbStar(Object.assign([],state.tpmbList),action.data)
               }

The specific implementation is:

chrome-capture-2022-6-14.gif

Video detail page implementation

In the previous article, this page did not use real video, but only used pictures to occupy the place. This time I prepared the video, and realized the functions of playing and pausing the video. The video details page is also similar to the picture details page. Sliding down from the id of this video to realize the collection function

  1. Implement video placement and playback
// 点击实现视频的暂停播放
  const videoRef = useRef(null)  
  const [play,setPlay] = useState(false)  
  const onVideo = () =>{
    if(play) {
      videoRef.current.pause();
      setPlay(false)
    }else{
      videoRef.current.play();
      setPlay(true)
    }
  }
  //放置视频     
  <video 
            controls="controls" //进度条
            src={item.videos}
            onClick={onVideo} 
            ref={videoRef}
            loop
            autoPlay="autoplay" //自动播放
            muted="true"  //静音
            /> 
  1. Implement the collection function

The process is similar to the image template collection. It also passes the id into the data list and returns after modifying the data.

  • in the mapDispatchToPropsmiddledispatchaction
const mapDispatchToProps =( dispatch ) =>{
  return {
      changeSpmbListStar(data){
        dispatch(actionCreators.changeSpmbListStar(data))
      }
  }
}
  • Modify data according to id in reducer
// 修改收藏star1的值   
const changeSpmbStar =(list,id) => {
  let ind = list.findIndex(data => id == data.id);
  list[ind].star2 = !list[ind].star2;
  return list
}     
// 调用函数
  case actionTypes.CHANGE_SPMB_STAR:
                return {
                ...state,
                 spmbList:changeSpmbStar(Object.assign([],state.spmbList),action.data) //浅拷贝
                }

The specific effects are:

12.gif

Because the video link related to this camera is easily banned because of cross-domain, all the videos of this project are downloaded to the local for display, and because of this reason, gitPage cannot be realized, so I recorded a relatively complete video for you. Let's take a look~

26(1).gif

26(1).gif

concluding remarks

After this period of revision and improvement, the perfection of the project is one step closer! There are still many deficiencies, and the functions have not yet been realized. I will continue to work hard and continue to improve this project. If you have any suggestions, you can also tell me in the comment area. We will see you in the next issue~
(ps: The source network of this article, if there is any infringement , please contact me to delete)

Project source code address: react-camera.github.io/react-camer…

Guess you like

Origin juejin.im/post/7120441628170387486