React 组件:计时器例子

文章目录

    • 1. 将应用程序分解为组件
    • 2. 构建应用静态版本
    • 3. 哪些组件是有状态的
    • 4. 每个 state 应该在哪个组件里
    • 5. 硬编码初始化state
    • 6. 添加反向数据流
    • 7. 添加服务器通信

learn from 《React全家桶:前端开发与实例详解》
https://zh-hans.reactjs.org/tutorial/tutorial.html
https://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app

本章学习的目标是做一个定时器

1. 将应用程序分解为组件

在这里插入图片描述
在这里插入图片描述

2. 构建应用静态版本

app.js

class TimersDashBoard extends React.Component {render() {return (<div className='ui three column centered grid'><div className='column'><EditableTimerList/><ToggleableTimerForm isOpen={true}/></div></div>)}
}class EditableTimerList extends React.Component {render() {return (<div id='timers'><EditableTimertitle='学习react'project='react web'elasped='3600'runningSince={null}editFormOpen={false}/><EditableTimertitle='学习python'project='deep learning'elasped='3600'runningSince={null}editFormOpen={true}/></div>)}
}class EditableTimer extends React.Component {render() {if (this.props.editFormOpen) {return (<TimerFormtitle={this.props.title}project={this.props.project}/>)} else {return (<Timertitle={this.props.title}project={this.props.project}elasped={this.props.elasped}runningSince={this.props.runningSince}/>)}}
}class TimerForm extends React.Component {render() {const submitText = this.props.title ? 'Update' : 'Create'return (<div className='ui centered card'><div className='content'><div className='ui form'><div className='field'><label>Title</label><input type='text' defaultValue={this.props.title}/></div><div className='field'><label>Project</label><input type='text' defaultValue={this.props.project}/></div><div className='ui two bottom attached buttons'><button className='ui basic blue button'>{submitText}</button><button className='ui basic red button'>Cancel</button></div></div></div></div>)}
}class ToggleableTimerForm extends React.Component {render() {if (this.props.isOpen) {return (<TimerForm/>)} else {return (<div className='ui basic content center aligned segment'><button className='ui basic button icon'><i className='plus icon'/></button></div>)}}
}class Timer extends React.Component {render() {const elaspedString = helpers.renderElapsedString(this.props.elasped)return (<div className='ui centered card'><div className='content'><div className='header'>{this.props.title}</div><div className='meta'>{this.props.project}</div><div className='center aligned description'><h2> {elaspedString} </h2></div><div className='extra content'><span className='right floated edit icon'><i className='edit icon'/></span><span className='right floated trash icon'><i className='trash icon'/></span></div></div><div className='ui bottom attached blue basic button'>Start</div></div>)}
}ReactDOM.render(<TimersDashBoard/>,document.getElementById('content')
)

在这里插入图片描述
<ToggleableTimerForm isOpen={true}/> 改成 falseToggleableTimerFormrender 回返回 显示 plus icon 的分支
在这里插入图片描述

3. 哪些组件是有状态的

  • 是通过 props 从 父组件 传递进来的吗?是的话,它很可能不是 state
  • 不随时间变化,很可能不是 state
  • 可以根据其他 state 或 props 计算得到,不是 state

原则就是用尽可能少的 state

4. 每个 state 应该在哪个组件里

如何确定,步骤:

  • 标识基于该state渲染的组件
  • 查找state共同所有者
  • 较高层次的组件拥有该state
  • 找不到的话,创建新组件来保存 state,并置于层次结构的上方

5. 硬编码初始化state

props 可理解为 : 不可改变的 state

  • TimersDashBoard
class TimersDashBoard extends React.Component {state = {timers: [{title: 'react learning',project: 'web dev',id: uuid.v4(),elasped: 3600 * 1000,runningSince: Date.now(),},{title: 'python learning',project: 'ai dev',id: uuid.v4(),elasped: 30 * 1000,runningSince: null,}]}render() {return (<div className='ui three column centered grid'><div className='column'><EditableTimerListtimers={this.state.timers}/><ToggleableTimerForm isOpen={false}/></div></div>)}
}class EditableTimerList extends React.Component {render() {const timers = this.props.timers.map((timer) => (<EditableTimerkey={timer.id}id={timer.id}title={timer.title}project={timer.project}elasped={timer.elasped}runningSince={timer.runningSince}/>))return (<div id='timers'>{timers}</div>)}
}
  • 表单是有状态的
class EditableTimer extends React.Component {state = {editFormOpen: false,}render() {if (this.state.editFormOpen) {return (<TimerFormid={this.props.id}title={this.props.title}project={this.props.project}/>)} else {return (<Timerid={this.props.id}title={this.props.title}project={this.props.project}elasped={this.props.elasped}runningSince={this.props.runningSince}/>)}}
}class TimerForm extends React.Component {state = {title: this.props.title || '',project: this.props.project || '',}handleTitleChange = (e) => {this.setState({title: e.target.value})}handleProjectChange = (e) => {this.setState({project: e.target.value})}render() {const submitText = this.props.title ? 'Update' : 'Create'return (<div className='ui centered card'><div className='content'><div className='ui form'><div className='field'><label>Title</label><input type='text' value={this.state.title}onChange={this.handleTitleChange}/></div><div className='field'><label>Project</label><input type='text' value={this.state.project}onChange={this.handleProjectChange}/></div><div className='ui two bottom attached buttons'><button className='ui basic blue button'>{submitText}</button><button className='ui basic red button'>Cancel</button></div></div></div></div>)}
}class ToggleableTimerForm extends React.Component {state = {isOpen: false,}handleFormOpen = () => {this.setState({isOpen: true});}render() {if (this.state.isOpen) {return (<TimerForm/>)} else {return (<div className='ui basic content center aligned segment'><buttonclassName='ui basic button icon'onClick={this.handleFormOpen}><i className='plus icon'/></button></div>)}}
}

6. 添加反向数据流

TimerForm组件

  • 表单创建、更新计时器
  • 取消动作

让父组件拥有函数(在事件发生时决定采取什么行为),父组件 通过 props 将函数传递给 TimerForm

class TimerForm extends React.Component {state = {title: this.props.title || '',project: this.props.project || '',}handleTitleChange = (e) => {this.setState({title: e.target.value})}handleProjectChange = (e) => {this.setState({project: e.target.value})}handleSubmit = () => {this.props.onFormSubmit({id: this.props.id,title: this.state.title,project: this.state.project,})}render() {const submitText = this.props.id ? 'Update' : 'Create'return (<div className='ui centered card'><div className='content'><div className='ui form'><div className='field'><label>Title</label><input type='text' value={this.state.title}onChange={this.handleTitleChange}/></div><div className='field'><label>Project</label><input type='text' value={this.state.project}onChange={this.handleProjectChange}/></div><div className='ui two bottom attached buttons'><button className='ui basic blue button'onClick={this.handleSubmit}>{submitText}</button><button className='ui basic red button'onClick={this.props.onFormClose}>Cancel</button></div></div></div></div>)}
}
  • ToggleableTimerForm 组件
class ToggleableTimerForm extends React.Component {state = {isOpen: false,}handleFormOpen = () => {this.setState({isOpen: true});}handleFormClose = () => {this.setState({isOpen: false});}handleFormSubmit = (timer) => {this.props.onFormSubmit(timer);this.setState({isOpen: false,})}render() {if (this.state.isOpen) {return (<TimerFormonFormSubmit={this.handleFormSubmit}onFormClose={this.handleFormClose}/>)} else {return (<div className='ui basic content center aligned segment'><buttonclassName='ui basic button icon'onClick={this.handleFormOpen}><i className='plus icon'/></button></div>)}}
}
  • TimersDashBoard 组件
	handleCreateFormSubmit = (timer) => {this.createTimer(timer);}createTimer = (timer) => {const t = helpers.newTimer(timer);this.setState({timers: this.state.timers.concat(t),})}render() {return (<div className='ui three column centered grid'><div className='column'><EditableTimerListtimers={this.state.timers}/><ToggleableTimerFormonFormSubmit={this.handleCreateFormSubmit}/></div></div>)}

在这里插入图片描述
在这里插入图片描述
还不能开始,删除,修改

  • 更新计时器
    编辑 Timer 组件,添加函数 onEditClick
						<spanclassName='right floated edit icon'onclick={this.props.onEditClick}><i className='edit icon'/></span>
  • EditableTimer 组件
	state = {editFormOpen: false,}handleEditClick = () => {this.openForm()}handleFormClose = () => {this.closeForm()}handleSubmit = (timer) => {this.props.onFormSubmit(timer);this.closeForm();}closeForm = () => {this.setState({editFormOpen: false});}openForm = () => {this.setState({editFormOpen: true});}

这些函数作为 props 向下传递

	render() {if (this.state.editFormOpen) {return (<TimerFormid={this.props.id}title={this.props.title}project={this.props.project}onFormSubmit={this.handleFormSubmit}onFormClose={this.handleFormClose}/>)} else {return (<Timerid={this.props.id}title={this.props.title}project={this.props.project}elapsed={this.props.elapsed}runningSince={this.props.runningSince}onEditClick={this.handleEditClick}/>)}}
  • EditableTimerList 添加 onFormSubmit={this.props.onFormSubmit}
class EditableTimerList extends React.Component {render() {const timers = this.props.timers.map((timer) => (<EditableTimerkey={timer.id}id={timer.id}title={timer.title}project={timer.project}elapsed={timer.elapsed}runningSince={timer.runningSince}onFormSubmit={this.props.onFormSubmit}/>))return (<div id='timers'>{timers}</div>)}
}
  • 顶层组件 TimersDashBoard
	handleEditFormSubmit = (attrs) => {this.updateTimer(attrs);}updateTimer = (attrs) => {this.setState({timers: this.state.timers.map((timer) => {if (timer.id === attrs.id) {return Object.assign({}, timer,{title: attrs.title,project: attrs.project,})}else{return timer;}})})}

属性传递给 EditableTimerList

					<EditableTimerListtimers={this.state.timers}onFormSubmit={this.handleEditFormSubmit}/>

删除计时器

Timer

	handleTrashClick = () => {this.props.onTrashClick(this.props.id)}。。。<span className='right floated trash icon'onClick={this.this.handleTrashClick}><i className='trash icon'/></span>

向上传递 EditableTimer

		else {return (<Timerid={this.props.id}title={this.props.title}project={this.props.project}elapsed={this.props.elapsed}runningSince={this.props.runningSince}onEditClick={this.handleEditClick}onTrashClick={this.props.onTrashClick} // add/>)}

向上传递 EditableTimerList

			<EditableTimerkey={timer.id}id={timer.id}title={timer.title}project={timer.project}elapsed={timer.elapsed}runningSince={timer.runningSince}onFormSubmit={this.props.onFormSubmit}onTrashClick={this.props.onTrashClick}  // add/>

TimersDashBoard 顶层实现这个删除函数

	handleTrashClick = (timerId) => {this.deleteTimer(timerId);}deleteTimer = (timerId) => {this.setState({timers: this.state.timers.filter(t => t.id !== timerId)})}。。。<EditableTimerListtimers={this.state.timers}onFormSubmit={this.handleEditFormSubmit}onTrashClick={this.handleTrashClick} // add/>

在这里插入图片描述

  • 计时器还不能工作

Timer组件

	componentDidMount() {this.forceUpdateInterval = setInterval(() => this.forceUpdate(), 50)} // 每 50 ms 调用一次 forceUpdate,重新渲染componentWillUnmount() {clearInterval(this.forceUpdateInterval)}  // 停止 forceUpdateInterval 的间隔执行,计时器删除之前调用handleTrashClick = () => {this.props.onTrashClick(this.props.id)}render() {const elapsedString = helpers.renderElapsedString(this.props.elapsed, this.props.runningSince)return (。。。)}

添加启动,停止

	handleStartClick = () => {this.props.onStartClick(this.props.id)}handleStopClick = () => {this.props.onStopClick(this.props.id)}

在 Timer 的 return 最底部添加 新组件

				<TimerActionButtontimerIsRunning={!!this.props.runningSince}onStartClick={this.handleStartClick}onStopClick={this.handleStopClick}/>
  • TimerActionButton
class TimerActionButton extends React.Component {render() {if (this.props.timerIsRunning) {return (<divclassName='ui bottom attached red basic button'onClick={this.props.onStopClick}>Stop</div>)} else {return (<divclassName='ui bottom attached green basic button'onClick={this.props.onStartClick}>Start</div>)}}
}

这些事件需要在上层结构中传递

EditableTimer

				<Timerid={this.props.id}title={this.props.title}project={this.props.project}elapsed={this.props.elapsed}runningSince={this.props.runningSince}onEditClick={this.handleEditClick}onTrashClick={this.props.onTrashClick}onStartClick={this.props.onStartClick} // addonStopClick={this.props.onStopClick} // add/>

EditableTimerList

				<EditableTimerkey={timer.id}id={timer.id}title={timer.title}project={timer.project}elapsed={timer.elapsed}runningSince={timer.runningSince}onFormSubmit={this.props.onFormSubmit}onTrashClick={this.props.onTrashClick}onStartClick={this.props.onStartClick} // addonStopClick={this.props.onStopClick} // add/>

TimersDashBoard

	handleStartClick = (timerId) => {this.startTimer(timerId);}startTimer = (timerId) => {const now = Date.now();this.setState({timers: this.state.timers.map((timer) => {if(timer.id === timerId) {return Object.assign({}, timer, {runningSince: now})}else{return timer;}})})}handleStopClick = (timerId) => {this.stopTimer(timerId);}stopTimer = (timerId) => {const now = Date.now();this.setState({timers: this.state.timers.map((timer) => {if(timer.id === timerId) {const lastElapsed = now - timer.runningSince;return Object.assign({}, timer,{elapsed: timer.elapsed + lastElapsed,runningSince: null})} else {return timer;}})})}。。。<EditableTimerListtimers={this.state.timers}onFormSubmit={this.handleEditFormSubmit}onTrashClick={this.handleTrashClick}onStartClick={this.handleStartClick} //addonStopClick={this.handleStopClick}  //add/>

在这里插入图片描述

7. 添加服务器通信

上面的计时器状态不可以保存,需要保存在服务器上

见下一章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/470898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一、快速开始一个 MyBatis项目(详解)

1.0 概述 1.三层架构 界面层&#xff1a; 和用户打交道的&#xff0c; 接收用户的请求参数&#xff0c; 显示处理结果的。&#xff08;jsp &#xff0c;html &#xff0c;servlet&#xff09; 业务逻辑层&#xff1a; 接收了界面层传递的数据&#xff0c;计算逻辑&#xff0c;…

2013 ACM区域赛长沙 K Pocket Cube hdu 4801

题意:给了一个2*2的魔方..每步操作可以将任意一面翻转90度..现在问在N(<7)步内.最多能翻出几面相同的. 直接打表模拟每种翻转情况 1 #include<cstdio>2 #include<cstring>3 #include<algorithm>4 #include<iostream>5 using namespace std;6 7 int …

React 组件和服务器

文章目录1. 请求服务器数据2. 发送开始停止请求3. 发送创建、删除、更新请求learn from 《React全家桶&#xff1a;前端开发与实例详解》https://zh-hans.reactjs.org/tutorial/tutorial.htmlhttps://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app服…

二、MyBatis常用对象分析 封装工具类

1.0 MyBatis 对象分析 &#xff08;1&#xff09; Resources 类 Resources 类&#xff0c;顾名思义就是资源&#xff0c;用于读取资源文件。其有很多方法通过加载并解析资源文件&#xff0c;返回不同类型的 IO 流对象。 &#xff08;2&#xff09; SqlSessionFactoryBuilder…

iOS图片拉伸技巧

纵观移动市场&#xff0c;一款移动app&#xff0c;要想长期在移动市场立足&#xff0c;最起码要包含以下几个要素&#xff1a;实用的功能、极强的用户体验、华丽简洁的外观。华丽外观的背后&#xff0c;少不了美工的辛苦设计&#xff0c;但如果开发人员不懂得怎么合理展示这些设…

LeetCode 2341. 数组能形成多少数对

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始的整数数组 nums 。在一步操作中&#xff0c;你可以执行以下步骤&#xff1a; 从 nums 选出 两个 相等的 整数从 nums 中移除这两个整数&#xff0c;形成一个 数对 请你在 nums 上多次执行此操作直到无法继续执行。 返…

三、MyBatis 使用传统 Dao 开发方式

1.0 使用 Dao 的实现类,操作数据库 1.0.1 Dao 开发 &#xff08;0&#xff09;定义接口StudentDao 及创建接口的映射文件StudentDao .xm package com.zep.dao;import com.zep.domain.Student;import java.util.List;public interface StudentDao {List<Student> selec…

LeetCode 2347. 最好的扑克手牌

文章目录1. 题目2. 解题1. 题目 给你一个整数数组 ranks 和一个字符数组 suit 。你有 5 张扑克牌&#xff0c;第 i 张牌大小为 ranks[i] &#xff0c;花色为 suits[i] 。 下述是从好到坏你可能持有的 手牌类型 &#xff1a; "Flush"&#xff1a;同花&#xff0c;五…

四、MyBatis 框架 Dao 动态代理

1.1 步骤 &#xff08;1&#xff09; 去掉 之前编写的Dao 接口实现类 &#xff08;2&#xff09; getMapper 获取代理对象 只需调用 SqlSession 的 getMapper()方法&#xff0c;即可获取指定接口的实现类对象。该方法的参数为指定 Dao接口类的 class 值。 不使用工具类&…

五、深入理解Mybatis中的参数parameterType (传递一个简单参数,传递多个参数:@Param、使用自定义对象、按位置、使用Map)

1.1 parameterType parameterType: 接口中方法参数的类型&#xff0c; 类型的完全限定名或别名。这个属性是可选的&#xff0c;因为 MyBatis可以推断出具体传入语句的参数&#xff0c;默认值为未设置&#xff08;unset&#xff09;。接口中方法的参数从 java 代码传入到mapper…

六、封装 MyBatis 输出结果resultType、resultMap 以及 数据库表中列名和返回对象属性名不一致的2种解决方案(详解)

1.1 resultType resultType: 执行 sql 得到 ResultSet 转换的类型&#xff0c;使用类型的完全限定名或别名。 注意&#xff1a;如果返回的是集合&#xff0c;那应该设置为集合包含的类型&#xff0c;而不是集合本身。resultType 和 resultMap&#xff0c;不能同时使用。 A、…

API 接口批量测试

ApiPost 创建接口 导入要测试的数据 测试结果 ApiFox 创建接口 导入接口 导入测试数据&#xff0c;可以直接编辑&#xff0c;粘贴进来 测试结果

LeetCode 2342. 数位和相等数对的最大和

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始的数组 nums &#xff0c;数组中的元素都是 正 整数。请你选出两个下标 i 和 j&#xff08;i ! j&#xff09;&#xff0c;且 nums[i] 的数位和 与 nums[j] 的数位和相等。 请你找出所有满足条件的下标 i 和 j &#xff…

软件项目管理-构建之法-四周总结

写在前面 课程名&#xff1a;软件项目管理 授课人&#xff1a;东北师范大学 杨贵福&#xff08; http://www.cnblogs.com/younggift/&#xff09; 教材&#xff1a;《构建之法 - 现代软件工程》 作者&#xff1a;邹欣老师 &#xff08;博客&#xff1a;http://www.cnblogs.com…

一、Vue基础语法学习笔记系列——插值操作(Mustache语法、v-once、v-html、v-text、v-pre、v-cloak)、绑定属性v-bind(绑定class、style)、计算属性

一、插值操作 1. Mustache 如何将data中的文本数据&#xff0c;插入到HTML中呢&#xff1f; 我们已经学习过了&#xff0c;可以通过Mustache语法(也就是双大括号)。 Mustache: 胡子/胡须. 我们可以像下面这样来使用&#xff0c;并且数据是响应式的 2. v-once 但是&#xff0…

LeetCode 2348. 全 0 子数组的数目

文章目录1. 题目2. 解题1. 题目 给你一个整数数组 nums &#xff0c;返回全部为 0 的 子数组 数目。 子数组 是一个数组中一段连续非空元素组成的序列。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,0,0,2,0,0,4] 输出&#xff1a;6 解释&#xff1a; 子数组 [0] 出现了…

二、Vue基础语法学习笔记——事件监听v-on、条件判断(v-if、v-else-if、v-else、v-show)、循环遍历(v-for遍历数组对象,key属性、检测数组更新)、图书案例、双向绑定

四、事件监听 在前端开发中&#xff0c;我们需要经常和用于交互。 这个时候&#xff0c;我们就必须监听用户发生的时间&#xff0c;比如点击、拖拽、键盘事件等等 在Vue中如何监听事件呢&#xff1f;使用v-on指令 v-on介绍 作用&#xff1a;绑定事件监听器 缩写&#xff1a; 预…

LeetCode 2352. 相等行列对

文章目录1. 题目2. 解题1. 题目 给你一个下标从 0 开始、大小为 n x n 的整数矩阵 grid &#xff0c;返回满足 Ri 行和 Cj 列相等的行列对 (Ri, Cj) 的数目。 如果行和列以相同的顺序包含相同的元素&#xff08;即相等的数组&#xff09;&#xff0c;则认为二者是相等的。 示…

三、Vue组件化开发学习笔记——组件化的基本步骤、全局组件和局部组件、父组件和子组件、注册组件的语法糖、模板分离写法、组件的数据存放

一、什么是组件化&#xff1f; 人面对复杂问题的处理方式&#xff1a; 任何一个人处理信息的逻辑能力都是有限的 所以&#xff0c;当面对一个非常复杂的问题时&#xff0c;我们不太可能一次性搞定一大堆的内容。 但是&#xff0c;我们人有一种天生的能力&#xff0c;就是将问题…

LeetCode 2353. 设计食物评分系统(sortedcontainers)

文章目录1. 题目2. 解题1. 题目 设计一个支持下述操作的食物评分系统&#xff1a; 修改 系统中列出的某种食物的评分。返回系统中某一类烹饪方式下评分最高的食物。 实现 FoodRatings 类&#xff1a; FoodRatings(String[] foods, String[] cuisines, int[] ratings) 初始化…