先上一张图片

功能实现

既然React的卖点是组件化(话说昨天面试小姐姐问React除了组件化还有什么,我居然忘了声明式,悔しい)。上图是自己的划分思路:

  1. 首先粉色部分(①区)显示的是实时汇率,初始值是美元和欧元的汇率比,当改变④或⑤,或者点击⑥时,①区根据当前状态进行实时切换,而②和③区的改变是不会影响①区的;
  2. ②和③区可以输入数字,根据目前选定的汇率,来实时换算货币值,比如就上图来说,在②区输入10,③区会实时变成8.12304;
  3. ④和⑤区是整个组件的核心,它们的值决定着其他各组件的方方面面;
  4. 当点击⑥区时,④和⑤当前选定的值会交换,②区值不会变,但因为汇率发生了变化,③区的值将发生变动,此时①区的汇率也将发生反转;
  5. 当然最外层的App类控制api的异步加载。

关于API

这里必须得多说几句,简直炸毛。

首先是在fixer申请了key,还不错,看下图。然鹅坑爹的是,他们家不提供货币全称API(比如USD对应United States Dollor;CNY对应Chinese Yuan酱紫)。虽然做这个项目只需要实时汇率就足够了,但是只显示干巴巴的简称肯定是不友好滴。

然后又去了currencylayer申请了key,货币全称倒是有了,看下图,但他家的汇率api超级坑爹,免费用户只能以美金为base货币,不像fixer无限制。遂两家api一起用...

具体实现

App类

名字懒得起了,就叫App好了,这个类主要用来异步加载两个json文件,第一次用到了fetch,听说能代替传统的Ajax,与之类似的还有什么axois、superagent,忙过这几天依次做一下研究。

class App extends React.Component {constructor(props) {super(props);this.state = {ratesObj: '', countriesObj: ''};}componentDidMount() {// 注意fetch的语法,其实跟Promise差不多少this.getCurrencyRates = fetch(this.props.api.getCurrencyRatesAPI).then(res => {res.json().then(resJSON => this.setState({ratesObj: resJSON.rates}));});this.getCountries = fetch(this.props.api.getCountriesAPI).then(res => {res.json().then(resJSON => this.setState({countriesObj: resJSON.currencies}));});}componentWillUnmount() {this.getCurrencyRates.abort();this.getCountries.abort();}render() {return (<div><CountryChoice ratesObj={this.state.ratesObj} countriesObj={this.state.countriesObj}/></div>)}
}ReactDOM.render(// 这里的api是个对象,存放上面所说的两个url,这里就不贴出来了<App api={api}/>,document.getElementById('root'),
);    

实时汇率展示类

这个类就是个受,自己啥都干不了。当select元素触发onchange事件抑或button被点击(触发onclick事件)时,这里就会发生变动。这个类只需要接受CountryChoice类(就是上面说到的核心类)的三个参数,分别是示意图中④和⑤区正在被选中的那两个币种,还有就是两个币种之间的汇率。

class CurrentExchangeRate extends React.Component {constructor(props) {super(props);}render() {return (<h1>1 <spanclassName="exchange-country-name">{this.props.firstSelectedCountry}</span> = {this.props.latestRates + ' '}<span className="exchange-country-name">{this.props.secondSelectedCountry}</span></h1>);}
}

exchange按钮类

这个类接受一个Boolean类型的flag,flag的定义同样是在CountryChoice类里面,每点击一次按钮,flag值就会从true和false之间切换,然后通过this.props.buttonToggle这个方法将实时的flag值传递回CountryChoice类,当然this.props.buttonToggle方法定义在CountryChoice类里,下面会说到。

// exchange按钮交换两个select,同时会改变 实时汇率展示 模块
class ChangeCountry extends React.Component {constructor(props) {super(props);this.state = {currentFlag: this.props.currentFlag};this.buttonClick = this.buttonClick.bind(this);}buttonClick() {// 一定要把 !this.state.currentFlag 先存到一个变量,再把这个变量赋值到setState里// 否则第一次点击按钮还是true,第二次才变成falseconst currentFlag = !this.state.currentFlag;this.setState({currentFlag: currentFlag});this.props.buttonToggle(currentFlag);}render() {return (<button className="button" onClick={this.buttonClick}>Exchange</button>)}
}

金额输入类

首先先写一个isNumber方法,是为了阻止用户输入非数字,且只能输入一个小数点。因为有两个input元素,所以先给两个元素命名。因为在任意一个input中输入值都会实时影响到另一个,所以这里就涉及到了<mark>状态提升</mark>问题,可以去研究官方文档-状态提升的这个例子。


// 命名两个input输入框
const inputNames = {f: 'firstInput', s: 'secondInput'};function isNumber(input) {if (/^[0-9]+([.][0-9]*)?$/.test(input) === false) {return input.slice(0, -1)} else {return input}
}class MoneyInput extends React.Component {constructor(props) {super(props);this.handleChange = this.handleChange.bind(this);}handleChange(ev) {this.props.onInputChange(isNumber(ev.target.value));}render() {const inputName = this.props.inputName;const inputValue = this.props.inputValue;return (// 这里用到了ES6的计算属性名<input name={inputNames[inputName]} className="input-value" type="text" value={inputValue}placeholder="0" onChange={this.handleChange}/>);}
}

币种(国家)选择类

高潮来了,额,重点来了。这个类控制着币种的选择,解释都放在了注释里,代码写得有点儿绕,都是exchange按钮惹的祸。写下来发现React的一个重点就是React 组件间通讯:

  • 【父组件】向【子组件】传值;
  • 【子组件】向【父组件】传值;
  • 没有任何嵌套关系的组件之间传值(如:兄弟组件之间传值)

这里推荐两篇文章,一个是淘宝前端团队的React 组件间通讯;另一个是在SegmentFault的一篇文章React 组件之间如何交流,把人家演示的例子敲一敲,基本上就能理解了。

// 货币选择:初始化第一个选中的是美刀,第二个选中的欧元
// 货币选择的变化影响着 汇率展示 和 汇率计算
class CountryChoice extends React.Component {constructor(props) {super(props);// 初始化状态this.state = {firstSelectedCountry: 'USD',// 第一个默认被选中的币种是美金secondSelectedCountry: 'EUR',// 第二个默认被选中的币种是欧元flag: true,// 立一个flag,初始是true,button被点击时在true和false之间切换inputName: 'f',// 默认选中的是第一个input标签inputValue: ''// 默认input标签的值是空};this.handleChange = this.handleChange.bind(this);this.buttonToggle = this.buttonToggle.bind(this);this.firstInputChange = this.firstInputChange.bind(this);this.secondInputChange = this.secondInputChange.bind(this);}// 通过ChangeCountry类传递过来的flag值来设置成当前状态buttonToggle(flag) {this.setState({flag: flag})}// 设置select标签的状态// 当flag是true时,把firstSelectedCountry的状态设置为name属性为“first-select”的value,// 把secondSelectedCountry的状态设置为name属性为“second-select”的value// 当flag是true时,把firstSelectedCountry的状态设置为name属性为“first-select”的value,// 把secondSelectedCountry的状态设置为name属性为“second-select”的value// 也就是说当flag是false时,此时name属性为“first-select”的select标签控制的是name属性为“second-select”的select标签// 当然我这里设计的不合理,应该通过动态修改name值才好,放在下一次的项目迭代吧,留个坑先handleChange(ev) {const target_name = ev.target.name;if (this.state.flag) {if (target_name === 'first-select') {this.setState({firstSelectedCountry: ev.target.value});} else if (target_name === 'second-select') {this.setState({secondSelectedCountry: ev.target.value});}} else {if (target_name === 'first-select') {this.setState({secondSelectedCountry: ev.target.value});} else if (target_name === 'second-select') {this.setState({firstSelectedCountry: ev.target.value});}}}// 获取第一个input输入的值firstInputChange(inputValue) {this.setState({inputName: 'f', inputValue});}// 获取第二个input输入的值secondInputChange(inputValue) {this.setState({inputName: 's', inputValue});}render() {const inputName = this.state.inputName;const inputValue = this.state.inputValue;// 因为要用到用户输入的值乘以汇率来进行计算// 当用户清空某个input标签的值时,这里就成了NaN// 这个函数就是当检测到输入的值为空时,自动设为数字0// 啊啊啊,肯定有更好的方法function formatInputValue(inputValue) {if (inputValue === '') {inputValue = 0;return inputValue} else {return parseFloat(inputValue)}}// 这边就写的很笨重了,汇率是根据flag的状态定的// 如果是true,汇率是第二个select标签选中的值除以第一个select// 假设当前在第一个input输入数值,那么下面的 inputName === 'f' 就是true, 所以第二个input的值(sI)就会被实时计算// 反正就是很绕,如果不加exchange按钮要省很多事儿,一切都是为了学习...const fI = inputName === 's' ? formatInputValue(inputValue) * (!this.state.flag ? this.props.ratesObj[this.state.secondSelectedCountry] / this.props.ratesObj[this.state.firstSelectedCountry] : this.props.ratesObj[this.state.firstSelectedCountry] / this.props.ratesObj[this.state.secondSelectedCountry]) : inputValue;const sI = inputName === 'f' ? formatInputValue(inputValue) * (this.state.flag ? this.props.ratesObj[this.state.secondSelectedCountry] / this.props.ratesObj[this.state.firstSelectedCountry] : this.props.ratesObj[this.state.firstSelectedCountry] / this.props.ratesObj[this.state.secondSelectedCountry]) : inputValue;return (<div className="container">{/*这边就是把当前状态(两个被选中的货币全称和之间的汇率)传递给①区来显示*/}<CurrentExchangeRatefirstSelectedCountry={this.state.flag ? this.props.countriesObj[this.state.firstSelectedCountry] : this.props.countriesObj[this.state.secondSelectedCountry]}secondSelectedCountry={!this.state.flag ? this.props.countriesObj[this.state.firstSelectedCountry] : this.props.countriesObj[this.state.secondSelectedCountry]}latestRates={this.state.flag ? this.props.ratesObj[this.state.secondSelectedCountry] / this.props.ratesObj[this.state.firstSelectedCountry] : this.props.ratesObj[this.state.firstSelectedCountry] / this.props.ratesObj[this.state.secondSelectedCountry]}/>{/*当在第二个input输入数字时,换算出来的值会实时显示在第一个input里*/}<div className="item"><MoneyInputinputName='f'inputValue={fI}onInputChange={this.firstInputChange}/>{/*传统设置默认选项是在option标签设置selected=selected, 现在放在select标签里,当然还有个Select的三方库*/}{/*通过map将option标签循环添加到第一个select标签里面*/}<select className="select" name="first-select"value={this.state.flag ? this.state.firstSelectedCountry : this.state.secondSelectedCountry}onChange={this.handleChange}>{Object.keys(this.props.ratesObj).map((key) => (<option key={key.toString()}value={key}>{key} - {this.props.countriesObj[key]}</option>))}</select></div><div className="item">// 当在第一个input输入数字时,换算出来的值会实时显示在第二个input里<MoneyInputinputName='s'inputValue={sI}onInputChange={this.secondInputChange}/>{/*通过map将option标签循环添加到第一个select标签里面*/}<select className="select" name="second-select"value={!this.state.flag ? this.state.firstSelectedCountry : this.state.secondSelectedCountry}onChange={this.handleChange}>{Object.keys(this.props.ratesObj).map((key) => (<option key={key.toString()}value={key}>{key} - {this.props.countriesObj[key]}</option>))}</select>{/*exchange按钮 将当前flag值传递给ChangeCountry类,同时将ChangeCountry类改变的flag值作为参数,通过buttonToggle方法传回当前这个类*/}<ChangeCountry currentFlag={this.state.flag} buttonToggle={flag => this.buttonToggle(flag)}/></div></div>)}
}

 写在最后

项目写的很丑,随着学习的深入还要进行迭代,加油吧。项目传到了github上了,没有劳驾node服务器,fork下来直接打开html文件就能使用了。

至于为什么当时想到了做一个汇率计算器,可能就是下图吧:

以上、よろしく。

汇率计算器 by React相关推荐

  1. 手把手教你撸一个Web汇率计算器

    手把手教你撸一个Web汇率计算器 前言 前段时间刚接触到前端网页开发,但是对于刚入门的小白而言,像flask.Django等这类稍大型的框架确实不太适合,今天这个Dash是集众家之长于一体的轻量化We ...

  2. 利用android实现汇率计算器,利用python编写一个汇率计算器

    利用python编写一个汇率计算器 发布时间:2020-11-10 15:03:44 来源:亿速云 阅读:137 作者:Leah 这篇文章运用简单易懂的例子给大家介绍利用python编写一个汇率计算器 ...

  3. python汇率转换代码_python 实现一个图形界面的汇率计算器

    调用的api接口: 完整代码 import requests from tkinter import * import tkinter as tk from tkinter import ttk cl ...

  4. python汇率换算程序_Python学习笔记8——汇率计算器

    前两次,我们初步学习了Python的图形界面库Tkinter,这一次,我们来试着做一个汇率换算计算器. 还是老样子,先把上次的代码弄过来: fromtkinterimport* root = Tk() ...

  5. 用计算机怎么算泰铢,工行泰铢汇率计算器

    ❶ 美元汇率计算器:1千美元是多少泰铢 货币兑换 1美元=35.39泰铢 1000美元=35390泰铢 数据仅供参考,交易时以银行柜台成交价为准 更新时间:2017-01-21 21:23 ❷ 国内 ...

  6. 基于php汇率接口调用实例,js调用API实时汇率计算器代码

    [实例简介]js调用API实时汇率计算器代码,支持 AED,ARS,AUD,BGN,BRL,BSD,CAD,CHF,CLP,CNY,COP,CZK,DKK,DOP,EGP,EUR,FJD,GBP,GT ...

  7. HTML+CSS+JS实现简易汇率计算器(使用Fetch)

    还是github上找的小玩意跟着模仿着敲的. 首先看一下fetch,我也是学过ajax之后头一次见这玩意,然后就看着人家代码顺便上MDN看了一下. Fetch API 提供了一个 JavaScript ...

  8. [jQuery.FQcomputer] 分期商城汇率计算器

    2019独角兽企业重金招聘Python工程师标准>>> ##开发需求分析 屌丝逆袭 期数 借款人费用 投资人收益 3个月 0.033 0.007 6个月 0.028 0.008 9个 ...

  9. 手机计算机在线汇率准确吗,汇率计算机(汇率计算器在线计算)

    [换算结果] 1 日元 兑换为 人民币 当前汇率是 0.067 ,换算结果为 0.067 [换算结果] 1 韩币 兑换为 人民币 当前汇率是 0.0084 ,换算结果为 0.0084 那么 1000日 ...

最新文章

  1. Oracle442个应用场景---------PL/SQL基础
  2. python客户端和服务端实验_python实现socket客户端和服务端简单示例
  3. 不同场景下MySQL的迁移方案
  4. oracle描述dept,一些关于oracle驱动表的描述
  5. RMAN之一:快速入门
  6. C# 关闭当前窗口打开另一窗口
  7. 获取结构体某成员偏移
  8. Eclipse的Git插件Egit: merge合并冲突具体解决方法
  9. ORB feature to FAST,定向快速旋转简报
  10. Endnote x7怎么在word中显示的处理方法
  11. Unity3d/2d手机游戏开发第二版 (金玺曾) 随书资源
  12. 宣传单印刷价格明细报价的影响因素有哪些?
  13. 计算机网络知识之1M宽带下载速度多少?
  14. RN:真机调试无线调试
  15. “入门大数据分析:探索海量数据的奥秘“
  16. 浪潮服务器重置密码方法
  17. 建议收藏 | 数据化、信息化、数字化、智能化到底都是指什么?彼此有什么联系?
  18. 我的世界圈服务器领地显示无效领地,我的世界领地圈地指令大全 Residence领地插件指令...
  19. CoffeeScript系列教程(一)—概述
  20. 这篇AI相关的ppt质量极高,强烈推荐

热门文章

  1. el-select 远程搜索时 没有箭头图标
  2. 【每日力扣10】有效的数独
  3. Heterogeneous Graph Neural Network(异质图神经网络)
  4. 第三方CNAS验收评测的重要意义
  5. matlab中class和whos作用
  6. 树莓派 查看当前cpu温度
  7. Mybatis注解开发指北
  8. android 手机短信恢复,Android短信如何恢复
  9. ae导出html,动画还原100%-AE一键导出
  10. 计算机是怎样跑起来在线看,计算机是怎样跑起来的