【React】学习笔记(一)——React入门、面向组件编程、函数柯里化

2022-10-29 17:30:40 浏览数 (1)

课程原视频:https://www.bilibili.com/video/BV1wy4y1D7JT?p=2&spm_id_from=pageDriver

目录

  • 一、React 概述
    • 1.1、React 开发背景
    • 1.2、模块与组件、声明式与组件化
    • 1.3、虚拟DOM与真实DOM
  • 二、React 入门
    • 2.1、Hallo React
    • 2.2、JSX语法规则
    • 2.3、JS语句(代码)与JS表达式的区别
  • 三、面向组件编程
    • 3.1、函数式组件
    • 3.2、类式组件
    • 3.3、组件实例的三大核心属性
      • 3.3.1、state
      • 3.3.2、props
      • 3.3.3、refs
    • 3.4、React 中的事务处理
  • 四、收集表单数据
    • 4.1、非受控组件
    • 4.2、受控组件
    • 4.3、总结
  • 五、高阶函数_函数柯里化
    • 5.1、高阶函数的定义
    • 5.2、函数柯里化
    • 5.3、案例分析
    • 5.4、不用柯里化的写法

学前需掌握以下知识点

  • 判断this的指向
  • class(类)
  • ES6语法规范
  • npm/yarn包管理器
  • 原型、原型链
  • 数组常用方法
  • 模块化

一、React 概述

用于构建用户界面的 Javascript 库,它主要专注于界面与视图。采用组件化模式、声明式编码,提高开发效率及组件复用率。在React Native中可以使用React语法进行移动端开发。使用虚拟DOM 优秀的Diffing算法,尽量减少与真实DOM的交互。

1.1、React 开发背景

由Facebook开发,且开源

  1. 起初由 Facebook的软件工程师 Jordan Walke 创建
  2. 于2011年部署于Facebook的newsfeed
  3. 随后在2012年部署于Instagram
  4. 2013年5月宣布开源 ...

近十年“陈酿”被各大厂广泛使用

1.2、模块与组件、声明式与组件化

模块 随着业务逻辑增加,代码越来越多且复杂。人们更倾向于将复杂大块的业务逻辑拆分成小模块,每个模块复杂一部分内容。可以理解为向外提供特定功能的js程序,一般就是一个js文件。这样写的好处是复用js,简化了js的编写,提高了js运行效率

声明式 React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React 能高效更新并渲染合适的组件。

以声明式编写 UI,可以让你的代码更加可靠,且方便调试。

组件 用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)。一个界面的功能太复杂,而且资源浪费也很多。React将各个不同的功能拆分为组件,每个组件只负责特定区域中的数据展示,如Header组件只负责头部数据展示。这样写可以复用代码,简化项目编码,提高运行效率

组件化

构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI。

由于组件逻辑使用 JavaScript 编写而非模板,因此你可以轻松地在应用中传递数据,并保持状态与 DOM 分离。

1.3、虚拟DOM与真实DOM

当我们需要修改DOM属性时,真实DOM是将一个新的界面直接覆盖在旧界面上,原来页面上已经有的数据就浪费了,假如原来有100条数据,当数据发生变化了就得产生100 n个DOM

而React 怎么做的呢,当数据发生变化时,将真实DOM生成对应虚拟DOM,但并不会将原来的虚拟DOM丢弃,它会进行虚拟DOM的比较,如果一样的话就不会给他生成真实的DOM,同样100条数据,发生变化了,它只会渲染多出来的数据

关于虚拟DOM 本质是Object类型的对象(一般对象)。虚拟DOM比较‘轻’,真实DOM比较‘重’,因为虚拟DOM是React内部在用,无需真实DOM上那么多属性。虚拟DOM最终会转换成真实DOM,呈现在页面上。

二、React 入门

准备工作 1.创建项目文件 2.下载依赖包

链接:https://pan.xunlei.com/s/VN3NvR6dEbf7OToBaxa14HWYA1?path=ids6

3.引入依赖包 将上面三个文件托到我们项目中,注意!引用必须按照以下顺序进行

代码语言:javascript复制
<! -- 引入react核心库-->
<script type="text/javescript" src=" ../React学习/季s/react.development. js"> </script>
<l --引入react-dom,用于支持react操作DOM -->
<script type="text/javescript" src="../React学习/3s/react-dom.development.js"></script>
<! --引入babel.用于将Jsx转为js-->
<script type="text/javescript" src="../React 学习/Js/babel.min.js"></script>

引入react.development 、 react-dom.development 这两个库后,全局变量中多了React 和 ReactDOM两个变量。

2.1、Hallo React

开始写代码

代码语言:javascript复制
<script type="text/babel">/*此处一定要写babel */
  //1.创建虚拟DOM
  const VDOM = <h1>Hello,React</h1>/* 此处一定不要写引号,因为不是字符串*/
  //2.渲染虚拟DOM到页面
  ReactDOM.render(VDoM,document.getElementById( 'test' ))
</script>

script标签的type属性必须写text/babel,如果不写默认为JavaScript

运行效果

2.2、JSX语法规则

在React 中的语法是JSX 并不是JS 有一定区别。 ①:定义虚拟DOM时,不要使用引号

代码语言:javascript复制
const VDOM = (
    <h2 id="myId">
        <span>myData</span>
    </h2>
)

这是个错误示范,这样输出的结果就是,myData还是有,但h2标签id为空

②:标签中混入JS表达式时要用{}

代码语言:javascript复制
const VDOM=(
      <h2 id={myId.toLowerCase()}>
         <span>{myData.toLowerCase()}</span>
      </h2>
)

toLowerCase()这个API是将ID字符都变成小写的意思

③:样式的类名指定不要使用class,而是className

代码语言:javascript复制
.title{
        color:white;
}

const VDOM=(
    <h2 calssName="title" id={myId.toLowerCase()}>
        <span>{myData.toLowerCase()}</span>
    </h2>
)

因为class是ES6编码格式中的关键字,JSX为了避免发生冲突于是便这么写。像这样的设计React中还有许多

④:内联样式,要用style={{key:value}}的形式去写,否则直接报错

代码语言:javascript复制
const VDOM=(
    <h2 calssName="title" id={myId.toLowerCase()}>
        <span style={{color:'while',fontsize:'29px'}}>{myData.toLowerCase()}</span>
    </h2>
)

⑤:只有一个根标签

向上面这样写就会飘红,因为跟标签是(h2)并且有两个,需要一个容器给包起来

代码语言:javascript复制
const VDOM=(
      <div>
         <h2 calssName="title" id={myId.toLowerCase()}>
             <span style={{color:'while',fontsize:'29px'}}>{myData.toLowerCase()}</span>
        </h2>
        <h2 calssName="title" id={myId.toLowerCase()}>
          <span style={{color:'while',fontsize:'29px'}}>{myData.toLowerCase()}</span>
        </h2>
     </div>
)

⑥:标签必须闭合 与Html中的标签同理,必须有头有尾,或者写成自结束标签,如<input/>

⑦:标签首字母 React中非常讲究细节。 1)若小写字母开头

  • 将改标签转为html同名元素,若html中无该标签的同名元素,则报错

2)若大写字母开头

  • react就去渲染对应的组件,若组件没有定义,则报错

JSX中写注释格式 {/ 代码块 /}

JSX语法小练习 需求: 动态展示如下列表

代码语言:javascript复制
//模拟一些数据
const data=['Angular','React','Vue']

const VDOM=(
    <div>
        <h1>前端js框架列表</h1>
        <ul>
        {//还记得上面的JSX语法规则嘛:标签中混入JS表达式时要用{}
         //但是并没有说JS代码哦,所以这里我们for循环直接传值写
         //map(vaule,key)加工数组
            data.map((item,index)=>{
                return <li key={index}>{item}</li>
          })
        }
        </ul>
    </div>
)

2.3、JS语句(代码)与JS表达式的区别

表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方,如

代码语言:javascript复制
 (1). a
 (2). a b
 (3). demo(1)
 (4). arr.map()
 (5). function test () {}

语句(代码):

代码语言:javascript复制
 (1) . if() {}
 (2) . for() {}
 (3) . swich() {case:xxxx}

React 接收到数组数据时会自动帮我们遍历,如果传的是对象会报错 Object are not valid as a React child

三、面向组件编程

官方给了我们两种组件化编程的方式:

3.1、函数式组件

从简到难。我们回顾一下什么是组件,组件:用来实现局部功能效果的代码和资源的集合,那一个组件里面就得包含 布局(html)、样式(css)、交互(js)、资源(image)等等。

所谓函数式组件如字面意思,使用函数的形式编写组件。该组件具有函数的一些特征

代码语言:javascript复制
<script type="text/babel">
    function MyComponent(){
        return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
  }

  ReactDom.render(<MyComponent/>,document.getElementById('test'))
  /*
    执行ReactDom.render(<MyComponent/>,document......之后发生什么?
      1.React解析组件标签,找到MyComponent组件。
      2.发现组件是使用函数定义的,随后调用该函数
      3.将返回的虚拟DOM转化为真实DOM,随后呈现在页面中
  */
</script>

注意事项

  1. 开头字母大写(小写会被判断为html标签
  2. 组件标签必须闭合
  3. 函数必须有返回值
  4. render()方法的第一个参数注意写组件标签,不要直接写组件名字
  5. babel转意时开启严格模式,禁止this指针指向window

3.2、类式组件

在学习类式组件之前我们先复习一下类的基本知识

  1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写
  2. 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的
  3. 类中所定义的方法,都放在了类的原型对象上,供实例去使用
代码语言:javascript复制
<script type="text/babel">
  //1.创建类式组件
  class MyComponent extends React.Component {
    render(){
      //render是放在哪里的?——MyComponent的原型对象上,供实例使用
      //render中的this是谁?——MyComponent的实例对象/MyComponent组件实例对象
        return <h2>我是用类式定义的组件(适用于【复杂组件】的定义)</h2>
    }
  }
    //渲染组件到页面
  ReactDom.render(<MyComponent/>,document.getElementById('test'))
  /*
    执行ReactDom.render(<MyComponent/>,document......之后发生什么?
       1.React解析组件标签,找到MyComponent组件。
       2.发现组件是使用类定义的,随后new出了该类的实类,并通过该实例调用到原型上的render方法
       3.将render返回的虚拟DOM转化为真实DOM,随后呈现在页面中
  */
</script>

注意事项

  1. render()API要写在类的开头
  2. 类式组件定义的类需要继承React.Component类
  3. 创建的类不要new实例或者写构造器,因为继承的类都帮我们写好了

3.3、组件实例的三大核心属性

3.3.1、state(状态)

state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)。

组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)

案例:如图点击后改变天气

代码语言:javascript复制
<script type="text/babel">
  //1.创建组件
  class Weather extends React.Component{
    constructor(props){//创建构造器
      super(props)
      //初始化状态
      this.state = {isHont:false} //state值一般转的是对象
    }
    render(){//重写父类的render()方法
      return <h1>今天天气很{this.state.isHont ? '炎热' : '凉爽'}</h1>
    }
    //2.渲染组件到页面
    ReactDOM.render(<Weather/>,document.getElementById('test'))
  }
</script>

上面的代码初始化了组件状态,通过判断isHont值就能判断返回炎热还是凉爽,现在我们只需要改变isHont值就可以完成上面的需求了,那如何改变isHont的值呢?

React中如何绑定事件 【复习】原生的三种事件绑定方法都可以进行事件判定,React官方推荐使用函数式绑定。类方法定义在类的原型对象上,供实例使用,通过类实例调用方法时,方法中的 this 指向的就是类实例。类中定义的方法在局部都开启了严格模式,直接调用不会指向window,所以值为undefined

React 不支持直接修改状态的属性,就算修改了React 本身也不作反馈

this.state. isHot = !isHot

需要借助setState这个API去更改

代码语言:javascript复制
<script type="text/babel">
  //1.创建组件
  class Weather extends React.Component{
    constructor(props){//创建构造器
      super(props)
      //初始化状态
      this.state = {isHont:false}
      //解决changWeather中this指向问题
      this.changWeather=this.changWeather.bind(this)
    }
      render(){//重写父类的render()方法
      //读取状态
      const {isHot}=this.state
      return <h1 onClick={this.changWeather}>今天天气很{isHont ? '炎热' : '凉爽'}</h1>
    }
    changWeather(){
      //获取原来的isHot值
      const isHot=this.state.isHot
      //严重注意:状态必须通过setState进行更改,且更新是一种合并,并不是替换
      this.setState({isHot:!isHot});
    }
    //2.渲染组件到页面
    ReactDOM.render(<Weather/>,document.getElementById('test'))
  }
</script>

this.setState 这个API操作是合并操作,而不是替换。构造器只在new实例时调用,render在每次状态更新和初始化的时候调用,只要我们通过合法的方式(this.setState API)更新组件的状态,React会自己帮我们调用render方法更新组件

state 的简写方式 【复习】类中可以直接写赋值语句

代码语言:javascript复制
<script type="text/babel">
  class Weather extends React.Component{
      //1.初始化状态
      state = {isHot:false,wind:'微风'}

      render(){
         const {isHot}=this.state
         return <h1 onClick={this.changWeather}>今天天气很{isHont ? '炎热' : '凉爽'}</h1>
    }
      //自定义方法——要用赋值语句的形式 箭头函数
      changWeather = ()=> {
        const isHot=this.state.isHot
        this.setState({isHot:!isHot});
      }
      ReactDOM.render(<Weather/>,document.getElementById('test'))
  }
</script>

【复习】箭头函数没有自己的this,所以他会往外部找this,所以函数里的this指向的其实Weather构造的实例对象

3.3.2、props

每个组件对象中对会含有props属性。组件标签的所有属性都保存在props中。通过标签属性从组件外向组件内传递变化的数据。组件内部不建议修改props的数据,数据的更新借助于state。

基本使用 需求: 自定义用来显示一个人员信息的组件

代码语言:javascript复制
<script type="text/babel">
  //创建组件
  class Person extends React.Component{
    render(){
      console.log(this);
      const{name,age,sex}=this.props
        return(
        <ul>
           <li>姓名:{name}</li>
           <li>性别:{sex}</li>
           <li>年龄:{age}</li>
        </ul>
      )
    }
  }
  //渲染组件到页面
  ReactDOM.render(<Person name="Tom" sex="女" age="18">,document.getElementById('test1'))
  ReactDOM.render(<Person name="JACK" sex="男" age="17">,document.getElementById('test2'))
</script>

在渲染组件时传入值,React会直接将其存在props属性上,但考虑到一个问题,如果某个对象属性非常多这样写就不是很聪明

批量传递

代码语言:javascript复制
<script type="text/babel">
  //创建组件
  class Person extends React.Component{
    render(){
      console.log(this);
      const{name,age,sex}=this.props
      return(
        <ul>
           <li>姓名:{name}</li>
           <li>性别:{sex}</li>
           <li>年龄:{age}</li>
        </ul>
      )
    }
  }
  const p={name:'老刘',age:18,sex:'女'}
  //渲染组件到页面
  ReactDOM.render(<Person {...p}>,document.getElementById('test2'))
</script>

对 props 进行限制 只有在Js原生的环境下我们才会谈数据类型,以及数据类型的判断,所以我们如果想传Number类型的数据到props时,要像下面这样写 ReactDOM.render(<Person age:{18}>,document.getElementById('test2'))

当我们的组件给别人使用时,别人不知道该往组件里传什么类型的属性,所以我们需要对props进行一些限制,React底层帮我们写好了我们需要按指定格式限制属性类型就可以了

代码语言:javascript复制
Person.propTypes{
    name:React.PropTypes.string
}

这种方式已经在React 15.xxxx 版本时被弃用了,16.xxx 版本需要引入依赖包prop-types.js

它有什么用呢?—— 用于对组件标签属性进行限制,下好然后在项目中引用

代码语言:javascript复制
Person.propTypes{
  name:PropTypes.string.isRequired,
  sex:PropTypes.string,
  age:PropTypes.number,
}
Person.defaultProps{
  sex:"男",
  age:18
}

Person.propTypes 对标签属性类型、必要性进行限制 语法:[属性名]:PropTypes.[数据类型].[必要性描述] 注意:数据类型都避开了原生的属性String、Number

Person.defaultProps 设置默认标签属性值

props 的简写方式 【注意】props是只读属性,只能get,不能set

代码语言:javascript复制
<script type="text/babel">
  //创建组件
  class Person extends React.Component{
    static propTypes{
         name:PropTypes.string.isRequired,
         sex:PropTypes.string,
         age:PropTypes.number,
        }
    static defaultProps{
         sex:"男",
         age:18
        }
    render(){}}
</script type="text/babel">

关于类式构造器传不传props 类中构造器可写可不写,如果写了构造器constructor必调super函数,而构造中传不传props取决于你需不需要在构造器中通过this访问props,必接必传

数式组件使用 props

代码语言:javascript复制
<script type="text/babel">
  //创建组件
  funciton Person(props){
    //限制标签类型和必要学
    return (
      <ul>
         <li>姓名:{name}</li>
         <li>性别:{sex}</li>
         <li>年龄:{age}</li>
      </ul>
    )
  }
    //函数式组件想使用限制器只能在外部设置
    Person.propTypes{
        name:PropTypes.string.isRequired,
        sex:PropTypes.string,
        age:PropTypes.number,
    }
    //默认值
    Person.defaultProps{
        sex:"男",
        age:18
    }
    //渲染组件到页面
    ReactDOM.render(<Person name='Tom' sex='男' age={18}/>,document.getElementById('test2'))
</script>

关于组件的三大核心属性,函数式组件只能使用props

3.3.3、refs

组件内的标签可以定义ref属性来标识自己。

字符串形式的ref

代码语言:javascript复制
<script type="text/babel">
  class Demo extends.React.Component{
    showData()=>{
      const {input1}=this.refs
      alert(input1.value)
    }
    showData2 =()=>{
      const {input2}=this.refs
      alert(input2.value)
    }
   render(){
      return
      <div>
        <input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
        <button ref="button100" onClick={this.showData}>点我提示左侧数据</button>&nbsp;
        <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
      </div>
    }
  }
</script>

组件里的标签可以通过ref属性来标识自己,然后都会收集到类实例的refs属性中,相当于原生中的id,但我们拿去值的方式也不原生中的document.getElementById,而是const{key值}=this.refs

【注意】 字符串的ref存在一些效率问题,如果写多了效率就不高,但方式简单,不过还是建议使用createRef API 和回调函数的ref

回调函数式的ref

代码语言:javascript复制
<script type="text/babel">
  class Demo extends.React.Component{
    showData()=>{
      const {input1}=this.refs
      alert(input1.value)
    }
    showData2 =()=>{
      const {input2}=this.refs
      alert(input2.value)
    }
    render(){
      return
      <div>
        <input ref={a => this.input1=a} type="text" placeholder="点击按钮提示数据"/>&nbsp;
        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
        <input ref={a => this.input2=a} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
      </div>
    }
  }
</script>

——关于回调函数的回调次数问题

在组件初始化的时候会执行一次,传入的是 DOM 元素

每次更新组件的时候都会调用两次回调函数,第一次传入值为null,第二次才传入参数DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但大多数情况下它是无关紧要的

代码语言:javascript复制
<script type="text/babel">
  class Demo extends.React.Component{
    saveInput=(c)=>
    {
      this.input=c;
      conosle.log('@',c);
    }
    render(){
      return
      <div>
        <input ref={this.saveInput} type="text" placeholder="点击按钮提示数据"/>&nbsp;
        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
      </div>
    }
  }
</script>

createRef 的使用

代码语言:javascript复制
<script type="text/babel">
  class Demo extends.React.Component{
     myRef=React.createRef()
     myRef2=React.createRef()

    //展示左侧输入框的数据
    showData =()=>{
        console.log(this.myRef.current.value);
    }

    //展示右侧输入框的数据
    showData2 =()=>{
        console.log(this.myRef2.current.value);
    }

    render(){
        return
      <div>
        <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
        <input ref={this.myRef2} onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
      </div>
    }
  }
</script>

【注意】React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的,只能存一个。这种方法繁琐的地方在于每次都要定义一个容器接受返回值,但也是官方最推荐的写法

四、收集表单数据

需求: 定义一个包含表单的组件,输入用户名密码后, 点击登录提示输入信息

4.1、非受控组件

代码语言:javascript复制
<script type="text/babel">
    class Login extends React.Component{
      handleSubmit =(event)=>{//event 事件对象
        event.preventDefault()//阻止表单提交
        const {username,password}=this
        alert(`你输入的用户名是:{username.value} 密码是:{password}`)
    }
    render(){
      return (
        <form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
         用户名: <input ref={c=>this.username=c}type="text" name="username"/>
         密码: <input ref={c=>this.password=c}type="password" name="password"/>
          <button>登录</button>
         </from>
      )
    }
    ReactDOM.render(<Login/>,document.getElementById('test'))
  }

</script>

非受控组件指的是,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。 在非受控组件中,可以使用一个ref来从DOM获得表单值。

非受控组件在底层实现时是在其内部维护了自己的状态state,这样表现出用户输入任何值都能反应到元素上。

4.2、受控组件

代码语言:javascript复制
<script type="text/babel">
   class Login extends React.Component{
    handleSubmit =(event)=>{//event 事件对象
     demo=(exent)=>{
       this.setState({username:event.trage})
       conosle.log("@");
     }
    }
    render(){
      return (
         <form action="http://www.baidu.com" onSubmit={this.handleSubmit}>
         用户名: <input onChange={this.demo} type="text" name="username"/>
         密码: <input type="password" name="password"/>
          <button>登录</button>
         </from>
      )
    }
    ReactDOM.render(<Login/>,document.getElementById('test'))
  }

</script>

在HTML中,表单元素的标签<input>、<textarea>、<select>等的值改变通常是根据用户输入进行更新。

在React中,可变状态通常保存在组件的状态属性中,并且只能使用 setState() 进行更新,而呈现表单的React组件也控制着在后续用户输入时该表单中发生的情况,以这种由React控制的输入表单元素而改变其值的方式,称为受控组件。

比如,给表单元素input绑定一个onChange事件,当input状态发生变化时就会触发onChange事件,从而更新组件的state。

受控组件更新state的流程 1、 可以通过初始state中设置表单的默认值 2、每当表单的值发生变化时,调用onChange事件处理器 3、事件处理器通过事件对象event拿到改变后的状态,并更新组件的state 4、一旦通过setState方法更新state,就会触发视图的重新渲染,完成表单组件的更新

React中数据是单项流动的,从示例中,可以看出表单的数据来源于组件的state,并通过props传入,这也称为单向数据绑定。然后又通过onChange事件处理器将新的数据写回到state,完成了双向数据绑定。

4.3、总结

受控组件 受控组件依赖于状态 受控组件的修改会实时映射到状态值上,此时可以对输入的内容进行校验 受控组件只有继承React.Component才会有状态 受控组件必须要在表单上使用onChange事件来绑定对应的事件 非受控组件 非受控组件不受状态的控制 非受控组件获取数据就是相当于操作DOM 非受控组件可以很容易和第三方组件结合,更容易同时集成 React 和非 React 代码

两者使用场景 1、受控组件使用场景:一般用在需要动态设置其初始值的情况。例如:某些form表单信息编辑时,input表单元素需要初始显示服务器返回的某个值然后进行编辑。

2、非受控组件使用场景:一般用于无任何动态初始值信息的情况。例如:form表单创建信息时,input表单元素都没有初始值,需要用户输入的情况。

五、高阶函数_函数柯里化

5.1、高阶函数的定义

当一个函数符合下面两个规范中的任何一个,那该函数就是高阶函数

  • 接受的参数是一个函数
  • 调用的返回值依然是一个函数

例如:Promise、setTimeout、arr.mup() 等等

5.2、函数柯里化

函数柯里化:通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式,刚刚的saveFormData其实也用到了函数的柯里化,那么我们来简单演示一下你就明白了

5.3、案例分析

在开发中我们常常会遇到注册账号的需求。用户输入用户名、密码。

代码语言:javascript复制
<script type="text/babel">
  class Login extends React.Component{//创建组件
      State={//初始化状态
        username='',//用户名
        password='',//密码
     }
     saveUsername=(exent)=>{//保存用户到状态中
        this.setState({username:event.target.value})
     }
     savePassword=(exent)=>{
        this.setState({password:event.target.value})
     }
     handleSubmit=(exent)=>{
        event.preventDefault()//阻止表单提交
        const{username,password}=this.state
        alert{`你输入的用户名是${username},你输入的密码是:${password}`}
     }
     render(){
        <from>
         用户名:<input onChange={this.saveUsername} type="text" name="username"/>
         密码:<input onChange={this.savePassword} type="password" name="password"/>
         <button>注册</button>
        </from>
     }
  }
</script>

这里可以看到saveUsername()和savePassword()这两条函数其实非常啰嗦,当需要保存的状态变多后不便于维护,如之后还要保存用户的身份证号、电话等信息。

【复习】对象的基本操作:在对象中想要拿到某个属性值名称需要使用 [ 属性名 ]

代码语言:javascript复制
let a = 'name'
let obj={} //{name:obj}
obj[a]='tom'

我们使用高阶函数来重写编写刚刚的需求

代码语言:javascript复制
<script type="text/babel">
  class Login extends React.Component{//创建组件
      state = {//初始化状态
      username='',//用户名
      password=''//密码
    }
    saveFormData=(dataType)=>{//保存表单数据到状态中
      return (event)=>{
      this.setState([dataType]:event.target.value)
      }
    }
    handleSubmit = (exent)=>{//表单提交的回调
      event.preventDefault()//阻止表单提交
      const{username,password}=this.state
      alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
    }
    render(){
        <from>
        用户名:<input onChange={this.saveFromData(username)} type="text" name="username"/>
        密码:<input onChange={this.saveFromData(password)} type="password" name="password"/>
        <button>注册</button>
      </from>
    }
  }
</script>

所谓函数柯里化是通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式,刚刚的saveFormData其实也用到了函数的柯里化,形式如下

代码语言:javascript复制
function sum(a){
   return (b)=>{
    return (c)=>{
     return a b c;
     }
  }
}

const result = sum(1)(2)(3)

不使用柯里化的写法

代码语言:javascript复制
<script type="text/babel">
  //创建组件
  class Login extends React.Component{
    //初始化状态
    state = {
      username='',//用户名
      password=''//密码
    }
    //保存表单数据到状态中
    saveFormData=(dataType,value)=>{
        this.setState([dataType]:value)
    }
    //表单提交的回调
    handleSubmit = (exent)=>{
      event.preventDefault()//阻止表单提交
      const{username,password}=this.state
      alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
    }
    render(){
        <from>
        用户名:<input onChange={event=>this.saveFromData('username',event)} type="text" name="username"/>
        密码:<input onChange={event=>this.saveFromData('password',event)} type="password" name="password"/>
        <button>注册</button>
      </from>
    }
  }
</script>

0 人点赞