入坑React系列五

写在前边

虽然是官网上的例子,但整体认真读一遍上手练一遍,还是收获满满,在学习过程还是多实践一些比较重要,加油呀,少年们!!

状态提升

遇到几个组件需要共用状态数据的情况,最好将这部分共享的状态提升至他们最近的父组件当中进行管理。

以一个实际例子展开。
需求是:开始呢我们会创建一个名为BoilingVerdict的组件,他会接受celsius这个温度变量作为他的props属性判断谁会不会开,接下来我们写一个名为 Calculator的组件。它会渲染一个 <input>来接受用户输入,然后将输入的温度值保存在 this.state.temperature 当中。

之后呢,它会根据输入的值渲染出 BoilingVerdict组件。

function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;
  }
  return <p>The water would not boil.</p>;
}

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    return (
      <fieldset>
        <legend>Enter temperature in Celsius:</legend>
        <input
          value={temperature}
          onChange={this.handleChange} />
        <BoilingVerdict
          celsius={parseFloat(temperature)} />
      </fieldset>
    );
  }
}

ReactDOM.render(
  <Calculator />,
  document.getElementById('root')
);


运行结果如图所示
image

添加第二个输入框,在输入的摄氏温度的基础上,添加一个华氏温度输入,并保持他们的同步

  • 通过从calculator组件中抽离一个TemperatureInput组件出来给他添加一个值为cf表示温度单位的属性,如下:
<!--这里-->
const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};


class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    const scale = this.props.scale;     //这里
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>   //这里
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}


  • 对Calculator 稍作修改,来渲染两个不同的温度输入框
class Calculator extends React.Component {
  render() {
    return (
      <div>
        <TemperatureInput scale="c" />
        <TemperatureInput scale="f" />
      </div>
    );
  }
}

但是此时一个输入框输入另一个并不会更新,而且现在不能从Calculator组件中展示BoilingVerdict的渲染效果

写出转换函数

  • 首先摄氏温度和华氏温度互相转换的函数
function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}
  • 上边只是单纯转换数字,tryConvert函数接受两个参数,一个是temperature变量,另外一个就是上边的单位转换函数,返回一个字符串,如下所示:
function tryConvert(temperature, convert) {
  const input = parseFloat(temperature);    //不是数字的话,返回空
  if (Number.isNaN(input)) {
    return '';
  }
  const output = convert(input);
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();    //保证小数点后三位
}
  • 到这里,两个TemperatureInput组件都是在自己的state中独立保存数据
class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    // ...  

但是我们想要的是这两个输入能够保持同步,当我们输入摄氏温度时华氏度这个框应该显示更新后的温度,反之亦然,如何做? 状态提升

  • 首先我们在TemperatureInput组件中将 this.state.temperature 替换为 this.props.temperature。现在,先假定 this.props.temperature 值已经存在,将来我们需要从 Calculator组件中传入。
render() {
    // 之前的代码: const temperature = this.state.temperature;
    const temperature = this.props.temperature;
    // ...

props是只读的,而之前temperature变量是保存在自身的state中的,TemperatureInput组件只需要调用this.setState()就可以改变它的值,但是现在temperature是作为prop从父组件传递下来的,也就是说TemperatureInput组件是没有控制权的

  • 之后,如果TemperatureInput组件希望更新温度时,就会调用this.props.onTemperatureChange组件
  handleChange(e) {
    // 之前的代码: this.setState({temperature: e.target.value});
    this.props.onTemperatureChange(e.target.value);
    // ...
  • 转向Calculator组件,将会保存当前输入的temperature和scale值到局部的state中 这样就可以保存最新输入的温度和单位,计算出另一个框中的值,如下所示:
class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};       //这里
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});   //这里
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});   //这里
  }

  render() {
  <!--//这里-->
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />     //这里

        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />  //这里

        <BoilingVerdict
          celsius={parseFloat(celsius)} />

      </div>
    );
  }
}

现在无论你编辑那一个输入框,另一个框总是基于这个值显示计算结果。


重新梳理整个过程

  • React在DOM的<input>上调用被指定为onChange的函数。在本例中,是TemperatureInput组件上的handleChange函数。
  • TemperatureInput组件的handleChange函数会用新输入值调用this.props.onTemperatureChange()函数。输入框组件的属性,包括onTemperatureChange都是由父组件Calculator提供的。
  • 当最开始渲染时,Calculator组件的handleCelsiusChange方法被指定给摄氏温度输入组件TemperatureInputonTemperatureChange方法,同时把handleFahrenheitChange方法指定给华氏输入组件TemperatureInputonTemperatureChange方法。所以Calculator组件的两个方法都会在相应输入框被编辑时被调用。
  • 在这些方法内部,Calculator组件会使用编辑输入的新值和当前输入框的温度单位来让React调用this.setState()方法来重渲染自身。
  • React调用Calculator组件的render方法来识别UI界面的样子。基于当前值和激活的温度单位,两个输入框的值会被重新计算。温度转换就是在这里被执行的。
  • React使用由Calculator指定的新props来调用各个TemperatureInput组件的render方法,React也会识别出子组件的UI界面。
  • React调用BoilingVerdict组件的render方法,传递摄氏温度作为它的属性。
  • React DOM 会用沸腾裁决更新DOM来匹配渴望的输入值。我们编辑的输入框获取新值,而另一个输入框则用经过转换的温度值进行更新。

完整代码如下:


const scaleNames = {
  c: 'Celsius',
  f: 'Fahrenheit'
};
function toCelsius(fahre){
  return (fahre - 32) * 5 / 9;
  
}
function toFahrenheit(celsius){
  return (celsius * 9 / 5) + 32;
  console.log("kkk")
}
function tryConvert(temperature,convert){
  const input = parseFloat(temperature);
  if(Number.isNaN(input)){
    return '';
  }
  const output = convert(input);
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();
}
function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>水会烧开</p>;
  }
  return <p>水不会烧开</p>;
}
class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    // this.state = {temperature: ''};
  }

  handleChange(e) {
    // this.setState({temperature: e.target.value});
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    // const temperature = this.state.temperature;
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

class Calculator extends React.Component {
  constructor(props){
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature:'',scale:'c'};
  }
  handleCelsiusChange(temperature){
    this.setState({scale:'c',temperature});
  }
   handleFahrenheitChange(temperature){
    this.setState({scale:'f',temperature});
  }
  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature,toCelsius):temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature,toFahrenheit):temperature;
    return (
      <div>
        <TemperatureInput scale="c" temperature = {celsius} onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput scale="f" temperature = {fahrenheit} onTemperatureChange={this.handleFahrenheitChange}/>
        
        <BoilingVerdict celsius={parseFloat(celsius)}/>
      </div>
    );
  }
}

ReactDOM.render(
  <Calculator />,
  document.getElementById('root')
);

可以使用react开发工具检查props属性,并且可以查看组件树,追踪代码源头
Monitoring State in React DevTools

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
旅游社交小程序功能有管理员和用户。管理员有个人中心,用户管理,每日签到管理,景点推荐管理,景点分类管理,防疫查询管理,美食推荐管理,酒店推荐管理,周边推荐管理,分享圈管理,我的收藏管理,系统管理。用户可以在微信小程序上注册登录,进行每日签到,防疫查询,可以在分享圈里面进行分享自己想要分享的内容,查看和收藏景点以及美食的推荐等操作。因而具有一定的实用性。 本站后台采用Java的SSM框架进行后台管理开发,可以在浏览器上登录进行后台数据方面的管理,MySQL作为本地数据库,微信小程序用到了微信开发者工具,充分保证系统的稳定性。系统具有界面清晰、操作简单,功能齐全的特点,使得旅游社交小程序管理工作系统化、规范化。 管理员可以管理用户信息,可以对用户信息添加修改删除。管理员可以对景点推荐信息进行添加修改删除操作。管理员可以对分享圈信息进行添加,修改,删除操作。管理员可以对美食推荐信息进行添加,修改,删除操作。管理员可以对酒店推荐信息进行添加,修改,删除操作。管理员可以对周边推荐信息进行添加,修改,删除操作。 小程序用户是需要注册才可以进行登录的,登录后在首页可以查看相关信息,并且下面导航可以点击到其他功能模块。在小程序里点击我的,会出现关于我的界面,在这里可以修改个人信息,以及可以点击其他功能模块。用户想要把一些信息分享到分享圈的时候,可以点击新增,然后输入自己想要分享的信息就可以进行分享圈的操作。用户可以在景点推荐里面进行收藏和评论等操作。用户可以在美食推荐模块搜索和查看美食推荐的相关信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值