React 之组件基础

133 阅读5分钟

一、React 创建组价组件的两种方式

1.函数组件

  • 使用JS 的函数或者箭头函数创建的组件
    • 为了区分普通标签,函数组件的名称必须 大写字母开头
    • 函数组件必须有返回值,表示该组件的结构
    • 如果返回值为 null,表示不渲染任何内容
    • 函数组件没有状态

2.类组件

  • 使用 ES6class 语法创建组件
    • 组件的名称必须是 大写字母开头

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

    • 类组件必须提供render方法

    • render 方法必须有返回值,表示该组件的结构

    • 类组件有状态

二、组件的基本使用

1、找一处风水宝地,创建启动项目

  • npx create-react-app react-com
  • cd react-com
  • yarn start 或 npm start 或 npm run start

2、保留入口文件 src/index.js 和 出口文件 src/App.js

image.png

3、index.js是入口文件,我们不动它

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
)

4、App.js 里面创建组件并导出

import React from 'react'

// ◆函数组件 1:使用函数创建组件
function FunCom1() {
  return <div>我是 函数组件 FunCom1</div>
}

// ◆函数组件 2:使用箭头函数创建组件
const FunCom2 = () => {
  return <div>我是 函数组件 FunCom2</div>
}

// ◆类组件
class ClassCom extends React.Component {
  render() {
    return <div>我是一个 类组件 ClassCom </div>
  }
}

function App() {
  return (
    <div className="App">
      app
      <FunCom1></FunCom1>
      <FunCom2></FunCom2>
      <ClassCom></ClassCom>
    </div>
  )
}
export default App

5、项目跑起来,看页面效果

image.png

三、组件的进阶使用

思考分析:

如果所有的组件都写在 App.js 里面,组件多了,会非常混乱。所以,我们的最优解是把每个组件放到单独的 JS 文件中,下面来试一试。

1.新建文件夹及文件

  • src/components/FunCom1.js
  • src/components/FunCom2.js
  • src/components/ClassCom.js

image.png

2.子组件抽离到成单独的 js 文件

FunCom1.js 普通函数组件

// ◆函数组件 1:使用普通函数创建组件
function FunCom1() {
    return <div>我是 函数组件 FunCom1</div>
}
export default FunCom1

FunCom2.js 箭头函数组件

// ◆函数组件 2:使用箭头函数创建组件
const FunCom2 = () => {
    return <div>我是 函数组件 FunCom2</div>
}
export default FunCom2

ClassCom.js 类组件

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    render() {
        return <div>我是一个 类组件 ClassCom </div>
    }
}
export default ClassCom

3.App.js 导入子组件

import React from 'react'
// 导入函数组件和类组件
import FunCom1 from './components/FunCom1'
import FunCom2 from './components/FunCom2'
import ClassCom from './components/ClassCom'

// ◆法1
// function App() {
//   return (
//     <div className="App">
//       app
//       <FunCom1></FunCom1>
//       <FunCom2></FunCom2>
//       <ClassCom></ClassCom>
//     </div>
//   )
// }

// ◆法2
// const App = () => {
//   return (
//     <div className="App">
//       app
//       <FunCom1></FunCom1>
//       <FunCom2></FunCom2>
//       <ClassCom></ClassCom>
//     </div>
//   )
// }

// ◆法3(一般用类组件比较多)
class App extends React.Component {
  render() {
    return <div className="App">
      app
      <FunCom1></FunCom1>
      <FunCom2></FunCom2>
      <ClassCom></ClassCom>
    </div>
  }
}
export default App

4.页面依然呈现

image.png

四、研究类组件 (ClassCom.js)

类组件通过 state 提供数据,只有类组件才有状态,函数组件没有状态

1.state 的使用

第一种方法:state 就是 状态

  • 定义状态:state={}
  • 渲染状态:this.state.变量名
import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆ state 的 写法1:
    state = {
        message: '快乐呀!'
    }
    render() {
        return <div>我是一个 类组件 ClassCom {this.state.message} </div>
    }
}
export default ClassCom

image.png

第二种方法:在构造函数中用 this.state= {} 来做初始化

  • 定义状态: constructor(){ super(); this.state = {} }
  • 渲染状态:this.state.变量名
import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆ state 的 写法2:
    constructor() {
        super()
        this.state = {
            message: '你好呀!'
        }
    }
    render() {
        return <div>我是一个 类组件 ClassCom {this.state.message} </div>
    }
}
export default ClassCom

image.png

2.函数调用执行顺序问题

  • 调用不带小括号:点击才会触发 {this.clickHandler1}
  • 调用带小括号,写成箭头函数形式:点击才会触发 {()=>{this.clickHandler2()}}
  • 调用带小括号:一进入页面就触发,可用于发请求等(点击不触发) {this.clickHandler3}
import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆ 点击事件函数
    clickHandler1() {
        console.log('点我呀1!')
    }
    clickHandler2() {
        console.log('点我呀2!')
    }
    clickHandler3() {
        console.log('点我呀3!')
    }
    render() {
        return <div>我是一个 类组件 ClassCom
            <br />
            <button onClick={this.clickHandler1}>按钮1</button>
            <button onClick={() => { this.clickHandler2() }}>按钮2</button>
            <button onClick={this.clickHandler3()}>按钮3</button>
        </div>
    }
}
export default ClassCom

image.png

3.关于 this 指向问题

1.绑定点击事件,打印 this

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆ 点击事件函数
    clickHandler1() {
        console.log(this,'点我呀1!')
    }
    clickHandler2() {
        console.log(this,'点我呀2!')
    }
    clickHandler3() {
        console.log(this,'点我呀3!')
    }
    render() {
        return <div>我是一个 类组件 ClassCom
            <br />
            <button onClick={this.clickHandler1}>按钮1</button>
            <button onClick={() => { this.clickHandler2() }}>按钮2</button>
            <button onClick={this.clickHandler3()}>按钮3</button>
        </div>
    }
}
export default ClassCom

image.png

2.思考分析 this

通过测试打印 this 发现,以 this.clickHandler1 的形式调用, this 指向 undefined,而其他的可以打印 this,这是为什么?

  • 事件处理程序的函数式函数调用模式,在严格模式下,this 指向undefined
  • render 函数是被组件实例调用的,因此 render函数中的this指向当前组件

4.解决类组件中 this 指向 undefined 的问题

法1:函数改成箭头函数(推荐)

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆法1:解决 this 指向 undefined 问题
    clickHandler = () => {
        console.log(this, 'clickHandler');// ◆改成箭头函数就可以打印 this
    }
    render() {
        console.log(this, 'render');// ◆这里一进入页面就可以打印一次 this
        return <div>我是一个 类组件 ClassCom
            <br />
            <button onClick={this.clickHandler}>按钮</button>
        </div>
    }
}
export default ClassCom

法2:在 constructor 里面通过 bind 修改 this 指向

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆法2:解决 this 指向 undefined 问题
    constructor() {
        super()
        this.clickHandler = this.clickHandler.bind(this)// ◆ 修改 this指向
    }
    clickHandler() {
        console.log(this, 'clickHandler')// ◆在 constructor 中改了 this 这里就可以打印
    }
    render() {
        console.log(this, 'render')// ◆这里一进入页面就可以打印一次 this
        return <button onClick={this.clickHandler}>按钮</button>
    }
}
export default ClassCom

法3:调用的时候,通过 bind 修改 this 指向

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆法3:解决 this 指向 undefined 问题
    clickHandler(){
        console.log(this,'clickHandler');// ◆调用的时候改了 this 这里就可以打印
    }
    render() {
        console.log(this,'render');// ◆这里一进入页面就可以打印一次 this
        return <button onClick={this.clickHandler.bind(this)}>按钮</button>
    }
}
export default ClassCom

法4:调用时以箭头函数的形式调用

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    // ◆法5:解决 this 指向 undefined 问题
    clickHandler(){
        console.log(this,'clickHandler');// ◆调用时以箭头函数的形式调用,就可以打印 this
    }
    render() {
        console.log(this,'render');// ◆这里一进入页面就可以打印一次 this
        return <button onClick={()=>{this.clickHandler()}}>按钮</button>
    }
}
export default ClassCom

5.组件的状态-修改状态

法1:this.setState({})

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    state={
        num:1
    }
    // ◆ 点击事件函数
    clickHandler=()=> {
        // 法1:this.stState({ })
        this.setState({
            num:this.state.num+1
        })
    }
    render() {
        console.log(this.state.num,'render');
        return <div>我是一个 类组件 ClassCom 
            <br />
            <p>数量变化:{this.state.num}</p>
            <button onClick={this.clickHandler}>按钮</button>
        </div>
    }
}
export default ClassCom

法2:this.setState(()=>{return {}})

image.png

import React from 'react'
// ◆类组件
class ClassCom extends React.Component {
    state={
        num:10
    }
    // ◆ 点击事件函数
    clickHandler=()=> {
        // 法2:this.setState(()=>{return {}})
        // 写成箭头函数的形式,参数 params 可以获取 state 中的数据变化
        this.setState((params)=>{
            console.log(params.num,'啦啦啦')
            return {
                num:this.state.num+10
            }
        })
    }
    render() {
        console.log(this.state.num,'render');
        return <div>我是一个 类组件 ClassCom 
            <br />
            <p>数量变化:{this.state.num}</p>
            <button onClick={this.clickHandler}>按钮</button>
        </div>
    }
}
export default ClassCom

image.png