React组件基础--两种创建方式,函数和类创建组件,抽离为独立 JS 文件,React 事件处理,有和无状态组件和组件中的 state 和 setState,事件绑定 this 指向的三种方法

目录

1.React组件介绍

2. React 组件的两种创建方式

1.使用函数创建组件

2.使用类创建组件

3.抽离为独立 JS 文件

步骤:

4.React 事件处理

1.事件绑定

2.获取事件对象

5.有状态组件和无状态组件

6.组件中的 state 和 setState

1.state的基本使用

2.初始化状态 

1.复杂写法初始化state

 2.简化写法初始化state(推荐) 

3.读取状态 

4.修改状态 

 React的状态不可变

7.从 JSX 中抽离事件处理程序

8. 事件绑定 this 指向

1.箭头函数

2.Function.prototype.bind()

3.class 的实例方法(推荐)

总结:


1.React组件介绍

组件表示页面中的部分功能;组合多个组件实现完整的页面功能;

特点:可复用、独立、可组合

2. React 组件的两种创建方式

1.使用函数创建组件

使用 JS 的函数(或箭头函数)创建的组件,就叫做函数组件

本质上就是js函数

该函数时一个有效的React组件,因为它接收唯一带有数据的“props”(代表属性)对象并且返回一个React元素

  • 约定1:函数名称必须以大写字母开头
  • 约定2:函数组件必须有返回值,表示该组件的结构;如果返回值为 null,表示不渲染任何内容
  • 渲染函数组件:用函数名作为组件标签名,
  • 组件标签可以是单标签也可以是双标签。(可以成对出现也可以自闭合)。
  • 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
//不要return,可以写
return null

组件名称必须以大写字母开头, React 据此区分 组件 和 普通的React 元素

使用函数名作为组件标签名

// 函数组件
function Hello(){
  return (
    <div>first</div>
  )
}
// 渲染组件
root.render(<Hello/>)

ReactDOM执行render,解析组件标签,使用函数定义的,调用函数,将返回的虚拟DOM转为真实DOM,渲染到页面 

也可以直接用箭头函数写

const Hello = () => <div>first</div>

2.使用类创建组件

  • 类组件:使用 ES6 的 class 创建的组件

  • 约定1:类名称也必须以大写字母开头

  • 约定2:类组件应该继承 React.Component 父类,从而可以使用父类中提供的方法或属性;

  • 约定3:类组件必须提供 render() 方法

  • 约定4:render() 方法必须有返回值,表示该组件的UI结构

// 定义类组件
class Hello extends React.Component{
  render(){
    return <div>ss</div>
  }
}
// 渲染组件
root.render(<Hello />)

导入文件的方式 

// 1.创建class组件
class Welcome extends React.Component {
      render () {//-->Welcome原型对象上
        console.log(this)//this指向的是实例化对象
        return <h2>Hello:{this.props.name}</h2>
      }
}

// 2.将class组件渲染到页面
ReactDOM.render(<Welcome name="React" />, document.getElementById('root')); 

 

 ReactDOM执行render,解析组件标签,使用类定义的,new Welcome-->产生实例化对象,通过实例对象去调用到原型上的render方法,将返回的虚拟DOM转为真实DOM,渲染到页面 

1.组合组件

// 1.创建class组件
class Welcome extends React.Component {
    render () {//-->Welcome原型对象上
        console.log(this)//this指向的是实例化对象
        return <h2>Hello:{this.props.name}</h2>
    }
} 
class App extends React.Component {
    render () {//-->Welcome原型对象上
        return (
          <div>
            <Welcome name="1" />
            <Welcome name="2" />
            <Welcome name="3" />
          </div>
        )
    }
}

// 2.将class组件渲染到页面
ReactDOM.render(<App />, document.getElementById('root')); 

 每一次使用组件都会实例化组件,并且传的值都是不一样的

 

3.抽离为独立 JS 文件

项目中的组件多了之后,该如何组织这些组件呢?

组件作为一个独立的个体,一般都会放到一个单独的 JS 文件

提取组件:将组建拆分成更小的组件,把复用的组件抽取出来,给多个组件使用

步骤:

  1. 创建Hello.js
  2. Hello.js 中导入React
    import React from 'react'
  3. 创建组件(函数 或 类)
    // 创建组件
    class Hello extends React.Component{
      render(){
        return(
          <div>抽离的js组件</div>
        )
      }
    }
    
  4. Hello.js 中导出该组件
    export default Hello
    
  5. index.js 中导入 Hello 组件
    import Hello from './Hello'
    
  6. 渲染组件
    root.render(<Hello />)
    

4.React 事件处理

react使用的是合成事件(二次封装)--- 为了更好的兼容性

import './App.css'
import { createRef } from 'react'
function App () {
  const myRef = createRef()
  function getValue () {
    console.log(myRef)
    console.log(myRef.current.value)
  }
  return (
    <div className='App'>
      <input type="text" ref={myRef} />
      <button onClick={getValue}>点击获取输入框的内容</button>
    </div>
  )
}

export default App

 

  

不建议使用ref,使用事件对象e最好 

1.事件绑定

方式:小驼峰(onXxxx) 

  • React 事件绑定语法与 DOM 事件语法相似

  • 语法:on+事件名称={事件处理程序}

    比如:

    <div onClick={()=>{}}></div>
  • 注意:React 事件采用驼峰命名法,比如:onMouseEnter、onFocus

  • 1.只需要一个额外参数

  • {clickHandler}变成  {()=>clickHandler('自定义参数')}
    
  • 2.既需要e也需要额外的参数
    {(e)=>clickHandler(e,'自定义参数')}
    
  • 在函数组件中绑定事件:

    function Hello() {
      function handleClick() {
        console.log('单击触发')
    
      }
      return (<button onClick={handleClick} > 单击 </button>)
    }
    
    class Hello extends React.Component {
      handleClick() {
        console.log('单击触发')
    
      }
      render() {
        return (<button onClick={this.handleClick} > 单击 </button>)
      }
    }
    
  • 案例
    // 函数组件
    function HelloFn () {
      // 定义事件回调函数(避免this指向问题)
      //这样写:回调函数中的this指向的是当前的组件实例对象
      //如何传递自定义参数
      const clickHandler = (e,msg) => {
        console.log('事件被触发了',e,msg)
        
      }
      return (
        // 绑定事件
        <button onClick={clickHandler}>click me!</button>
        //传递实参过来,在后面显示
        //事件被触发了 xx!
        <button onClick={(e)=>clickHandler(e,'xx!')}>click me!
        </button>
        //想要e和msg两个,则加e即可
        //显示
        //事件被触发了
        //xx!
      )
    }
    
    // 类组件
    class HelloC extends React.Component {
      // 定义事件回调函数
      clickHandler = () => {
        console.log('事件被触发了')
      }
      render () {
        return (
          // 绑定事件
          <button onClick={this.clickHandler}>click me!</button>
        )
      }
    }
    

2.获取事件对象

  1. 可以通过事件处理程序的参数获取到事件对象e
  2. React 中的事件对象叫做:合成事件(对象)
  3. 合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题
  4. e:JSX所接收的属性(attributes)以及子组件(children)
 function handleClick(e) {
    // 阻止浏览器的默认行为
    e.preventDefault();
    console.log('单击触发')
  }
  return (
    <a href="https://dayjs.fenxianglu.cn/" onClick={handleClick} > 单击 </a>
  )

通过e(官方称呼:props)可以传一些动态的值过去 

<div id="root"></div>
<script type="text/babel">
//函数组件
    function Welcome (e) {
      console.log(e)
      console.log(this)//undefined-->jsx的语法下,babel将模式变为严格模式(防止this指向window)
      return <h2>hello{e.name}</h2>
    }
    //将组件渲染到页面
   //在react中定义的函数名就是组件名
    ReactDOM.render(<Welcome name="React" />, document.getElementById('root'))
</script>

函数组件的ReactDOM做了什么:ReactDOM执行render,解析组件标签,调用函数,将返回的虚拟DOM转为真实DOM,渲染到页面

通过e.target得到发生事件的DOM元素对象,事件发生者是自己,获取的数据也是自身的数据---请勿过度使用ref,可以使用事件对象替代ref 

import './App.css'
function App () {
  function changeValue (e) {
    console.log(e)
    console.log(e.target.value)
  }
  return (
    <div className='App'>
      <input type="text" onChange={changeValue} />
    </div>
  )
}

export default App

 

5.有状态组件和无状态组件

能够为组件添加状态和修改状态的值。在react hook出来之前,函数式组件是没有自己的状态的,所以下面以类组件为例。

  1. 函数组件又叫做无状态组件,类组件又叫做有状态组件

  2. 状态(state)即数据

  3. 函数组件没有自己的状态,只负责数据展示(静)

  4. 类组件有自己的状态,负责更新 UI,让页面“动” 起来

    比如:计数器案例中,点击按钮让数值加 1 。

    0 和 1 就是不同时刻的状态,而由 0 变为 1 就表示状态发生了变化。状态变化后,UI 也要相应的更新。

    React 中想要实现该功能,就要使用有状态组件来完成。

6.组件中的 state 和 setState

props是单向的:所有React组件都必须像纯函数一样保护它们的props不被更改。只能用,不能修改

因为应用程序的UI是动态的,并会随着时间而变化,所以有了state。 

组件的数据挂载方式 :state,props,refs

1.state的基本使用

  1. 状态(state)即数据,是组件内部的私有数据,只能在组件内部使用

  2. 状态:组件描述某种显示情况的数据;由组件自身设置和更改,自己维护

  3. state 的值是对象,表示一个组件中可以有多个数据

  4. 状态即数据

  5. 通过 this.state 来获取状态

2.初始化状态 

  • 通过class的实例属性state来初始化
  • state的值是一个对象结构,表示一个组件可以有多个数据状态  

1.复杂写法初始化state

//构造器定义state对象
constructor(){
    // ES6要求必须要有super
    super()
    // 初始化state
    this.state = {
      Uname: 'zz'
    }
  }
}

 2.简化写法初始化state(推荐) 

class Welcome extends React.Component {
//直接将state对象定义在Welcome原型对象上
  // 初始化状态
  state = {
    Uname: 0
  }
  render() {
    return <h2>name:{this.state.Uname }</h2>
  }
}

// 2.将class组件渲染到页面
ReactDOM.render(<Welcome/>, document.getElementById('root')); 

3.读取状态 

  • 通过this.state来获取状态
render() {
  return (
     <div>有状态组件,{this.state.count}</div>
  )  
}

完整代码
class Counter extends React.Component {
  // 初始化状态
  state = {
    count: 0
  }
  render() {
    // 读取状态
    return <button>计数器{this.state.count}</button>
  }
}

4.修改状态 

不要直接修改状态的值,而是基于当前状态创建新的状态值

语法:this.setState({ 要修改的数据 })

注意:不要直接修改 state 中的值,这是错误的!!!改变state一定需要通过setState方法

// 错误
this.state.count += 1

//正确

class Counter extends React.Component {
  // 定义数据
  state = {
    count: 0
  }
 //setCount() {
 // console.log(this)-->undefined,严格模式下,因为这个函数作为回调函数而存在,调用是在全局调用
 // }
  // 定义修改数据的方法
  setCount = () => {//通过箭头函数指向实例对象
    console.log(this)
    this.setState({//参数可以是对象,也可以是函数
      count: this.state.count + 1
    })
  }
  // 使用数据 并绑定事件
  render () {
    return <button onClick={this.setCount}>{this.state.count}</button>
  }
}

参数为函数时 

setCount = () => {//函数
    this.setState((prevState,props) => {
        console.log(prevState) //上一个state
        console.log(props) //传值的数据
        return { count: this.state.count + 1 }
    })
}
ReactDOM.render(<Counter sex="nan" />, document.getElementById('root')); 

setState() 作用:1. 修改 state 2. 更新UI

思想:数据驱动视图,即只要修改数据状态,那么页面就会自动刷新,无需手动操作dom

<h1>计数器:{this.state.count}</h1>

<button onClick={() => {
       this.setState({
            count:this.state.count +1
       })
}}>+1
</button>

 React的状态不可变

1.错误的直接修改

state = {
  count : 0,
  list: [1,2,3],
  person: {
     name:'jack',
     age:18
  }
}
// 直接修改简单类型Number
this.state.count++
++this.state.count
this.state.count += 1
this.state.count = 1

// 直接修改数组
this.state.list.push(123)
this.state.list.spice(1,1)

// 直接修改对象
this.state.person.name = 'rose'

2.(正确)基于当前状态创建新值

this.setState({
    count: this.state.count + 1
    // 数组修改,在数组后面加4
    list: [...this.state.list, 4],
    // 对象修改,覆盖原来的name
    person: {
       ...this.state.person,
       // 覆盖原来的属性 就可以达到修改对象中属性的目的
       name: 'rose'
    }
})

3.(正确)数组删除 - filter

 // 删除2 
this.setState({
    list: this.state.list.filter(item => item !== 2)
})

5. 总结state的注意点

  1. render函数执行的次数-->初始渲染执行一次+页面改变次数-->1+n次
  2. render函数中this指向组件对象
  3. 状态数据,不能直接修改的,要通过setState(参数可以是对象,也可以是函数)
  4. 参数是函数时,函数需要返回一个对象;函数会有两个参数,第一个是上一次的state,第二个是props
  5. 在组件中自定义方法,如果使用普通的函数定义,里面的this是undefined;需要使用到箭头函数,this指向外部实例对象             

7.从 JSX 中抽离事件处理程序

  1. JSX 中掺杂过多 JS 逻辑代码,会显得非常混乱                                                                    推荐:将逻辑抽离到单独的方法中,保证 JSX 结构清晰
  2. 但是逻辑抽离到单独的方法中,会出现:事件处理程序中 this 的值为 undefined                  所以:this 指向组件实例(render方法中的this即为组件实例)

随着js标准的发展,主流的写法已经变成了class fields,无需考虑太多this问题。  

https://zh-hans.reactjs.org/docs/handling-events.html#gatsby-focus-wrapper

8. 事件绑定 this 指向

1.箭头函数

利用箭头函数自身不绑定this的特点

render() 方法中的 this 为组件实例,可以获取到 setState()

// 创建组件
class Hello extends React.Component {
  state = {
    count: 0
  }
  //事件处理程序
  onIncrement() {
    this.setState({
      count: this.state.count + 1
    })
  }
// 使用数据,并绑定事件
  render() {
    return (
      <div>
      // 箭头函数中的this指向外部环境,此处为:render()方法
        <div>count:{this.state.count}</div>
     //直接沿用父函数(render)中的this指向
        <button onClick={() => this.onIncrement()}>+1</button>
      </div>
    )
  }
}
 
//render函数中的this已经被react内部做了修正
//这里的this:
//指向当前的组件实例对象,箭头函数中的this,直接沿用
//所以也是指向组件的实例对象

2.Function.prototype.bind()

利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起。constructor中使用bind强行修正this指向。

相当于在类组件初始化的阶段,就可以把回调函数的this修正到永远指向当前组件实例对象。 

class Hello extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 0
    } 
   // 前面挂载到实例属性上  后面拿到的是onIncrement()方法
    this.onIncrement = this.onIncrement.bind(this)
  }
  //事件处理程序
  onIncrement() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <div>count:{this.state.count}</div>
        <button onClick={this.onIncrement}>+1</button>
      </div>
    )
  }
}

3.class 的实例方法(推荐)

利用箭头函数形式的class实例方法

注意:该语法是实验性语法,但是,由于babel的存在可以直接使用

class Hello extends React.Component {  
 //定义数据
  state = {
    count: 0
  }
  //事件处理程序
  //定义修改数据的方法
  onIncrement = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <div>count:{this.state.count}</div>
        <button onClick={this.onIncrement}>+1</button>
      </div>
    )
  }
}

总结:

  1. 推荐:使用class的实例方法
    class Hello extends React.Component {
    onIncrement = () => {
    this.setState({ … })
    }
    
  2. 箭头函数
     <button onClick={() => this.onIncrement()}>+1</button>
    
  3. bind
     constructor() {
        super()
        this.onIncrement = this.onIncrement.bind(this)
      }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值