接着上一章,脚手架已经搭建完毕,接下来便可以编写组件,先上效果图:

对苹果篮子页面进行组件切割

根据视图,我们可以将其切割为两部分

AppleItem:里面的苹果列表项

AppleBasket:包着苹果列表项的篮子

解析苹果篮子的数据结构,应该有一个苹果列表 apples(每个苹果具有id,重量,是否被吃掉的标志),是否在采摘苹果状态标记等,如下

const basket = {isPicker:false,apples:[{id:1,weight:230,isEaten:false},{id:2,weight:120,isEaten:true},......]
}

看一下我们的项目结构:红色圈起来的是这次组件编写需要动到的部分

具体组件如何编写,直接贴出代码

AppleItem:这是一个用于展示苹果的列表项,props只需传递进来具体一个苹果的数据apple和吃掉苹果的处理函数eatApple

import React from 'react';
import './index.less';
import '../../styles/common.less';
import appleImg from '../../assets/apple.png'
class AppleItem extends React.Component {render(){let {apple,eatApple} = this.props;return(<div className="apple-item row"><div className='row'><div className="apple"><img src={appleImg} alt="苹果图片"/></div><div className="info"><div className="name">红苹果 - {apple.id}号</div><div className="weight">{apple.weight}克</div></div></div><div className="btn-div"><button onClick={eatApple.bind(this,apple.id)}>吃掉</button></div></div>)}
}
export default AppleItem;

AppleBasket:这个稍微复杂点,但是也没那么复杂。props接收外界传递进来的就是整个苹果篮子的数据,以及所有的苹果处理函数的集合

拿到 整个苹果篮子的数据appleBasket,跟视图的数据结构是不符合,所以就需要将其转化为我们视图所需要需要的数据结构。map遍历appleBasket.apples,统计已吃跟未吃的苹果数量,重量,将未吃苹果push进新数组。未知苹果的数组就是我们要的数据。

import React from 'react';
import './index.less';
import '../../styles/common.less'
import AppleItem from '../AppleItem';class AppleBasket extends React.Component {render(){const {appleBasket,actions} = this.props;let stats = {isPicker:appleBasket.isPicker,eat:{num:0,weight:0},noeat:{num:0,weight:0},apples:[]}appleBasket.apples.map(elem => {let name = elem.isEaten?"eat":"noeat";stats[name].num++;stats[name].weight += elem.weight;if (!elem.isEaten) {stats.apples.push(elem);}})function getNoApple(){if (stats.apples.length===0 && !stats.isPicker) {return <div className='no-apple'>篮子空空如也,快去摘苹果吧</div>}return;}const that = this;function getApples(){let data = [];if (!stats.isPicker) {data.push(stats.apples.map((apple,index) => <AppleItem key={index} apple={apple} eatApple={that.props.actions.eatApple}/> ))}else{return <div className='no-apple'>正在采摘苹果...</div>}return data;}return(<div className="apple-basket"><div className="title">苹果篮子</div><div className="stats row"><div className='col current'><div className="label">当前</div><div><span>{stats.noeat.num}</span>个苹果,<span>{stats.noeat.weight}</span>克</div></div><div className='col eat'><div className="label">已吃掉</div><div><span>{stats.eat.num}</span>个苹果,<span>{stats.eat.weight}</span>克</div></div></div><div className="apple-list col">{getApples()}{getNoApple()}</div><div className="btn-panel row"><button className={stats.isPicker ? 'disabled' : ''} onClick={actions.pickApple}>摘苹果</button></div></div>)}
}
export default AppleBasket;

组件的编写就完成了。写完组件,可能有人会疑问,那我的props数据从哪里来,我怎么展示我已经写好的组件,这时候就需要我们写一个容器级(或页面级)组件,然后将应用的state作为props传递进去给子组件,当然,这些都是后话。就算没有这些操作,我们有storybook,这是一个可以对组件进行单元测试的有利工具。

启动storybook:npm run storybook

打开 http://localhost:9009便可以看到页面

要想在storybook看到我们编写的组件就需要在 stories/index文件里面add我们的组件

语法十分简单,模仿storybook提供的demo(Welcome/Button),照猫画虎,很容易就可以引入我们的组件

storiesOf('Apple',module).add("AppleItem",()=>(<AppleItem apple={apple} eatApple={action("eatApple")}/>)).add("AppleBasket",()=>(<AppleBasket appleBasket={basket} actions={appleActions}/>))

需要传递给组件的props数据需要我们事先编写,可以理解为我们的测试数据。最终完整的引入如下

import AppleItem from '../src/components/AppleItem/index'
import AppleBasket from '../src/components/AppleBasket/index'const basket = {isPicker:false,apples:[{id:1,weight:230,isEaten:false},{id:2,weight:120,isEaten:true},{id:3,weight:290,isEaten:false},{id:4,weight:118,isEaten:false},{id:5,weight:280,isEaten:true}]}
const apple = {id:3,weight:280,isEaten:false};
const appleActions = {eatApple: (id) => action("eatApple")(id),pickApple: () => action("pickApple")('摘苹果')
};
storiesOf('Apple',module).add("AppleItem",()=>(<AppleItem apple={apple} eatApple={action("eatApple")}/>)).add("AppleBasket",()=>(<AppleBasket appleBasket={basket} actions={appleActions}/>))

组件的编写便告一段落了

接下来就是如何在我们的页面里面展示苹果篮子实例以及数据处理 redux

2. 苹果篮子 redux处理

2.1 整个应用就是store,而应用数据的来源就是store里面的state,redux的作用就是处理state。redux主要分为两部分:

actions:actions分同步action跟异步action。同步action返回一个对象,异步action返回一个函数,在函数里面进行异步请求

reducers:根据actions类型的不同,分别进入到不同的处理函数,返回新的state

用户不能直接修改数据,只能触发通过触发action来修改数据,action就是一个定义好的普通对象,type表示动作类型,payload用来负载用户触发action携带的数据。

{

type:'PICK_APPLE',

payload:445

}

2.2 actions

苹果篮子实例,分别有吃苹果,摘苹果的动作,对应有 eatApple / pickApple 的action,还需注意有苹果数据初始化的隐藏action

在这里我将 pickApple作为异步处理,即摘到的苹果数据由后台返回,将eatApple做同步处理。这样通过比较同步和异步action就能很清楚的知道两者的区别。

同步action直接返回对象即可。但是异步action需要进行action切割:通知异步开始的action,异步成功的action,异步失败的action。

最终代码如下

import Apples from '../services';  //Apples 是已经封装好的异步请求接口let actions = {initApplesStart:function(){return function(dispatch,getState){Apples.init().then(function(res){dispatch(actions.initApplesSuccess(res));}, function(err){dispatch(actions.initApplesFail(err));});}},initApplesSuccess:(data)=>({type:'apple/INIT_APPLE_SUCCESS',payload:data}),initApplesFail:(data)=>({type:'apple/INIT_APPLE_FAIL',payload:data}),eatApple:(id) => ({type:'apple/EAT_APPLE',payload:id}),pickApple:function(){return function(dispatch,getState){if(getState().appleBasket.isPicker){return;}dispatch(actions.beginPickApple());Apples.pick().then(function(res){dispatch(actions.donePickApple(res.apples));}, function(err){dispatch(actions.donePickApple(err));});}},beginPickApple:() => ({type:'apple/BEGIN_PICK_APPLE'}),donePickApple:appleWeight => ({type:'apple/DONE_PICK_APPLE',payload:appleWeight}),failPickApple:err => ({type:'apple/FAIL_PICK_APPLE',payload:err})
}
export default actions;

2.3 reducers

reducers相对来说,简单点,其接收action,通过判断action类型的不同对state做不同的处理,然后返回新的state即可

const initialState = {isPicker:false,apples:[]
};const appleReducer = (state=initialState,action) =>{switch (action.type) {case 'apple/INIT_APPLE_SUCCESS':state = action.payload;return {...state};case 'apple/INIT_APPLE_FAIL':return state;case 'apple/EAT_APPLE':state.apples.map(elem => {if (elem.id===action.payload) {elem.isEaten = true;}})return {...state};case 'apple/BEGIN_PICK_APPLE':state.isPicker = true;return {...state};case 'apple/DONE_PICK_APPLE':state.isPicker = false;state.apples.push(...action.payload);return {...state};case 'apple/FAIL_PICK_APPLE':state.isPicker = false;return {...state};default:return state;}
}
export default appleReducer;

2.4 通常我们的应用不止一个 reducers,这时候需要将reducers进行整合。

import { combineReducers } from 'redux';
import appleReducer from './appleReducer';
//import todoReducers from './todoReducers';
const rootReducer = combineReducers({// todoItems:todoReducers,appleBasket: appleReducer
});export default rootReducer;

2.5 store

有了 reducers就可以创建 store了

在入口文件 src/index.js里createStore,然后将store注入我们的应用里面即可

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';import registerServiceWorker from './registerServiceWorker';
import reducer from './redux/reducers'
import Root from './pages/Root'let store = createStore(reducer,applyMiddleware(thunk))ReactDOM.render(<Root store={store}/>,document.getElementById('root')
);
registerServiceWorker();

2.6 编写页面级组件 Apples.js

通过 redux的connect函数可以将state映射到props,将actions和dispatch映射到props

import React from 'react'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import AppleBasket from '../components/AppleBasket/index';
import actions from '../redux/actions/appleAction';class Apples extends React.Component {constructor(props){  //初始化数据super(props);this.props.dispatch(this.props.actions.initApplesStart)}render(){let {appleBasket,actions,dispatch} = this.props;return(<div className="apples"><AppleBasket appleBasket={appleBasket} actions={actions}/></div>)}
}const mapStateToProps = state => ({appleBasket: state.appleBasket
});
const mapDispatchToProps = dispatch => ({dispatch: dispatch,actions: bindActionCreators(actions, dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(Apples);

2.7 编写项目根组件 root

import React from 'react';
import {Provider} from 'react-redux';import '../styles/common.less'
import Apples from '../pages/Apples'export default class Root extends React.Component{render(){const { store } = this.props;return(<Provider store={store}><Apples/></Provider>)}
}

最终 npm run start

可以在 localhost:3000看到苹果篮子实例成功跑起来了。

【React】React全栈脚手架搭建-苹果篮子示例相关推荐

  1. uniapp+egg.js+react实现全栈笔记App

    软件介绍 这是一个笔记App,主要使用的技术是uniapp+egg.js+react 软件预览图片: 首页: 编辑页面.png 编辑和登录页面 编辑页面2.png 项目地址: https://gith ...

  2. python3中数字类型有哪些_python全栈_009_Python3基本数据类型--列表(示例代码)

    1:列表的介绍 列表是python的基础数据类型之一 ,其他编程语言也有类似的数据类型. 比如C++中的数组, java中的数组等等. 它是以[ ]括起来, 每个元素用' , '隔开,而且可以存放各种 ...

  3. React+Egg.js实现全栈个人博客

    React+Egg.js实现全栈个人博客 这是一个个人博客软件,前台和后台使用的都是React,后端使用egg.js,地址 前台 文章列表 1.png 文章详情 2.png 后台管理系统 添加文章 3 ...

  4. 视频教程-React全栈:前后端分离的招聘Web App项目(含资料)-ReactJS

    React全栈:前后端分离的招聘Web App项目(含资料) 张长志技术全才.擅长领域:区块链.大数据.Java等.10余年软件研发及企业培训经验,曾为多家大型企业提供企业内训如中石化,中国联通,中国 ...

  5. 预告:Javascript全栈开发的系列文章

    自从一年前发布了Vuejs小书的电子书,也有些日子没有碰过它们了,现在因为项目的缘故,需要使用JavaScript全栈开发.所以,我得把这个全栈环境搭建起来. 说起来搭建JS全栈开发环境,设计到的东西 ...

  6. 急速JavaScript全栈教程

    自从一年前发布了Vuejs小书的电子书,也有些日子没有碰过它们了,现在因为项目的缘故,需要使用JavaScript全栈开发.所以,我得把这个全栈环境搭建起来. 整个系列,是会采用我的一贯风格,就是不疾 ...

  7. 全栈开发入门实战:后台管理系统

    这篇 Chat 将分享个人开发一个后台管理系统的全过程.后台管理系统,其实可以作为全栈开发的入门项目,因为它会涉及到前后台交互的很多知识点,做完一个后台管理系统,然后部署上线,你基本就算是入门全栈开发 ...

  8. 使能开发者、加速产业创新,全栈创新领域鲲鹏再结硕果

    文 | 曾响铃 来源 | 科技向令说(xiangling0815) 每一个开发者都了不起! 2021年4月26日,HDC.Cloud 2021落下帷幕,这次大会不管是在深圳大学城举办的主会场,还是清华 ...

  9. 最新python全栈3期高级开发工程师 独家完整版

    课程目录: │   ├─1-10 │  │   │  ├─day01 │  │      01 python全栈s3 day1 计算机发展史.mp4 │  │      02 python全栈s3 d ...

最新文章

  1. 群聊:项目级的错误处理
  2. mysql 取某个范围内随机日期
  3. Adb shell命令打电话测试4G
  4. 最大子段和问题Java实现
  5. Java:双向链表反转实现
  6. 大数据之-Hadoop3.x_MapReduce_TextInputFormat---大数据之hadoop3.x工作笔记0107
  7. org manual翻译--3.6 Org-Plot
  8. 有关 Unity UIElements 和 UIToolkit
  9. WPF:在XmlDataProvider上使用主-从绑定(Master-Detail Binding)
  10. Flex请求Php端的奇怪现象
  11. python同时输出名字和时间_Python练习小工具——根据Exif的拍摄时间和设备名批量重命名照片...
  12. 左耳朵耗子:你得知道,技术不是用来写 CRUD 的
  13. Zabbix报警语音
  14. 计算机网络最佳路由,计算机网络路由研究分析
  15. 联发科5G基带处理器MT6297(Helio M70)
  16. 5.9 使用新建调整图层命令调整图像 [原创Ps教程]
  17. server多笔记录拼接字符串 sql_第四章、SQL Server数据库查询大全(单表查询、多表连接查询、嵌套查询、关联子查询、拼sql字符串的查询、交叉查询)...
  18. 【练习】获取新浪搜索中的热搜榜的标题
  19. php 启动时候提示 PHP startup
  20. itext7 给PDF增加表单域并设置值

热门文章

  1. linux如何查看python的版本_linux怎么改python默认版本
  2. haproxy实现7层的负载均衡[haproxy+pacemaker+fencing]
  3. 2019联想小新pro13.3 Intel i7 10710U+MX250 liinux双系统安装及美化修改(ubuntu19.10 / ubuntu18.04.4 / ubuntu20.04)
  4. FireAlpaca(电脑绘画软件)官方中文版V2.7.7 | 火羊驼绘画软件下载 | 电脑绘画软件哪个好用?
  5. 格力迷局:“30亿元持股计划”员工不买账,董明珠“一石二鸟”?
  6. python api讲解_集合api讲解_Python C编程视频课程_Python视频-51CTO学院
  7. iPhone之随机数的生成
  8. CP2112使用USB转IIC通信教学示例
  9. 大学校园IP网络广播-厂家基于局域网的大学校园IP广播设计指南
  10. PHP规范PSR13(链接定义接口)介绍