react 基础知识整理

最近开始复习react特性了,这篇是用来记录复盘的

创建项目

npm i -g create-react-app
npx create-react-app react-demo
cd react-demo
npm start

jsx配置

要在jsx中自动补齐标签,需要在settings.json文件中添加以下配置

{ 
  "emmet.includeLanguages": {
      "javascript": "javascriptreact"
    }
 }

基础语法

Fragment

同vue一样,只允许·html中最外层只能有一个容器,可以写个div包含,如果不想让其在html中显示,可以使用Fregment替代(类似vue中的template标签),注意要使用大写开头

// 注意就算是里层的,只要是return返回的,最外层都只能有一个容器
import React, { Component, Fragment } from 'react'

class Todolist extends Component {
  render() {
    return (
      
        
  • 学英语
  • 学医学
) } }

自定义组件必须以大写字母开头

// 注意引入的组件,要以大写开头
ReactDOM.render(, document.getElementById('root'));

// 不能写小写
// ReactDOM.render(, document.getElementById('root'));

类组件的使用

  • constructor构造函数中接收父组件属性props,使用super继承属性,使用this.state设置自身的状态
  • 如果只返回一个标签内容,直接return
  • 如果有多个标签内容,则需要加括号包裹返回内容:return (...),且返回的内容最外层只能有一个容器
  • 返回的最外层容器,如果不想显示,则可以使用Fragment标签包裹,它的作用同vue中的template标签,只是作容器,但不会渲染为dom
import React,{ Component, Fragment } from 'react';

class HtmlContent extends Component{
  constructor(props) {
    super(props)
    this.state = {
      name: 'xxx'
    }
  }
  render() {
    const htmlContent = "这是html内容"
    const htmlObj = { __html: htmlContent }
    // 返回一个标签时,则不用()包裹
    return 

// return ( // // 多行内容return返回时用()包裹,且最外层只能有一个容器 // // Fragment相当于vue中的template作用 // //

多行内容

//

//
// ) } } export default HtmlContent

要在react中想使用js语法,需要使用{}

react中使用{}作为插值的标识符,等同于vue中的{{}},在其中可以任何js的表达式

css类名: className

css的类名使用className, 而不是class,因为reactjsx中认为class与类名会有二义,所以使用className,否则会警告:
Warning: Invalid DOM property `class`. Did you mean `className`?


注释

{/* 使用bind改变this指向 */}
或
{
  //使用bind改变this指向
}

使用不转义的html

使用dangerouslySetInnerHTML={{__html: xxx}},dangerouslySetInnerHTML返回是个包含html内容的对象,注意__html是规定的属性名

import React, { Component, Fragment } from 'react'

class HtmlCotent extends Component {
  render() {
    const htmlContent = '使用原生html'
    const rawHtmlContent = { __html: htmlContent}
    return (
      
        {/* html模板 */}
        

) } } export default HtmlCotent;

使用htmlFor实现点击某处,聚焦在其它处

render() {
  return (
    
      
{/* 使用bind改变this指向 */} {/* 想实现点击label时,聚焦在input框上,使用htmlFor实现 */}
) }

条件渲染

import React, {Component} from 'react'

class Condition extends Component {
  constructor(props) {
    super(props)
    this.state = {
      show: false,
      list: [
        {id: 1, age: 11},
        {id: 2, age: 22},
        {id: 3, age: 33},
      ]
    }
  }
  render() {
    // if else渲染
    if(this.state.show) {
      return 

hello

}else { return

byebye

} // 三元表达式 // return (this.state.show ?

hello

:

byebye

) // 与运算&&,或运算|| // return this.state.show &&

hello

// return this.state.show ||

byebye

} } export default Condition

suspense

suspense通常和异步组件配合,用于在异步组件未加载时,添加等待动画或其它操作等

import React,{ Component, Suspense } from 'react';
const AsyncComp = React.lazy(() => import('./FormInput'))
class SuspenseDemo extends Component {
  render() {
      // fallback代表异步操作之前的展示效果
     return Loading...
}> {/* 这里是异步引入的组件 */} } } export default SuspenseDemo;

循环渲染

使用map渲染,key的使用同vue,一般都不要设为indexrandom

    // 循环渲染
    return (
      
    {this.state.list.map(item => { return
  • {item.age}
  • } )}
)

state状态

  • 使用this.state定义状态,要使用this.setState({xxx: xxx})方式修改状态
  • state中数据不可直接使用this.state.xxx = xxx形式来改变状态,这是因为reactimmutable概念决定的,它规定了state中的数据不能被直接改变,必须使用setState方式修改,必须将state中数据拷贝一份来修改
import React, { Component } from 'react'

class SetStateDemo extends Component{
  constructor(props) {
    super(props)
    this.state = {
      count: 0,
      list: [
        {id: 1, age: 1},
        {id: 2, age: 2}
      ]
    }
  }
  addCount = () => {
    // 建议使用这种,对原数据进行拷贝,在拷贝上修改
    const newList = this.state.list.slice()
    newList.push(3)
    this.setState({
      // 使用this.state.count++,会报警告,因为这句直接修改了原count的值:
      // Do not mutate state directly. Use setState()
      // count: this.state.count++
      count: this.state.count + 1,
      list: newList
    })
    console.log(this.state.count) // 这里是异步的
  }
  render() {
    return (
      

{this.state.count}

) } } export default SetStateDemo;

事件

  • 事件要使用on + 大写驼峰的写法,比如 onClink={this.xxx},等同于vue中的@click="xxx"

  • 关于事件的this指向:

    1. 第一种写法:
      • 使用ES5方式定义事件,this会默认为undefined,要使用bind来绑定this指向,否则会报错Cannot read property 'setState' of undefined
      • 事件的bind(this)最好写在constructor中,这样只会执行一次bind并缓存结果,如果写在标签上,则点击一次就要执行一次bind,且多处使用时,多处也要bind,所以写在constructor中是一种性能优化
    2. 第二种写法,使用ES6箭头函数,this会默认指向当前实例,不需要考虑this指向,但可能需要babel来转译
import React, { Component } from 'react'

class Event extends Component {
  constructor(props) {
    super(props)
    this.state = {
      name: '小花',
    }
    // 第一种写法,绑定事件中的this
    this.handleClick = this.handleClick.bind(this)
  }
  render() {
    // 如果使用这句的话,当点击多次,就要触发多次bind
    // 在constructor中只bind一次,保存缓存结果,算是一种性能优化
    // return 

{this.state.name}

// 要使用onXxxx的写法绑定事件 return (
{/* 第一种写法 */}

{this.state.name}

{/* 第二种写法 */}

{this.state.name}

) } // 第一种定义方法 handleClick() { // 通过setState修改数据 this.setState({ name: '小小' }) } // 第二种定义方法写法,使用箭头函数,使this指向当前实例 handleClick2 = (event) => { console.log(event); // 通过setState修改数据 this.setState({ name: '小小' }) } } export default Event
  • 事件的性质
    1. react中,使用event获取当前事件,但看到打印出的是SyntheticBaseEvent,这是react合成的事件,并不是原生的Mouse Event,它模拟出了DOM事件的所有能力,包括冒泡、阻止事件等
    2. 可以通过event.nativeEvent获取原生事件
    3. 所有的事件,在react16以前,都被挂载到document上;在react17后,则会被挂载到root根元素上,这样有利于多个React版本并存,例如微前端;可以通过event.navtiveEvent.currentTarget来获取挂载元素
    4. 这和dom事件不一样,和vue事件也不太一样,vue的事件是原生的Mouse Event,并会被挂载到当前元素
render() {    
    return (
      
{/* react事件性质 */} 跳转链接
) }
  // react的事件
  handleClick3 = (event) => {
    console.log('handleClick3');
    // SyntheticBaseEvent,是react自身处理的合成事件,不是原生的Mouse Event
    // 这是跟vue不同的,vue的event是原生的,且被挂载到当前元素,通过$event获取
    console.log(event)
    event.preventDefault()  // 阻止原生操作,比如a标签的跳转,右链菜单等
    event.stopPropagation() // 阻止冒泡

    // 获取原生事件 MouseEvent
    console.log('event nativeEvent:', event.nativeEvent); 

    // 原生事件的对象,跳转链接
    console.log('nativeEvent target:', event.nativeEvent.target)  

    // 事件挂载的对象,react16前是挂载到document上的,react17后是挂载到root元素
    console.log('nativeEvent currentTarget:', event.nativeEvent.currentTarget ) 
  }
image.png
  • 事件的传参
  1. 使用bind传参,自定义参数从第二个开始传

事件传参一

// event是默认添加到最后一个形参中的
  handleClick4(a, b, event) {
    console.log('参数:', a, b, event);  // 参数: aa bb SyntheticBaseEvent
  }
  1. 使用箭头函数,event需要定义在返回的函数中

事件传参2

// 需要返回一个函数,event作为返回函数的参数
  handleClick5 = (a) => {
    return (event) => {
      console.log('a', a);  // aa
      console.log('e', event);  // SyntheticBaseEvent
    }
  }
  1. 在绑定时,使用箭头函数,将event参数传入,推荐使用这种,写法方便简洁一些

this.handleClick6('aa', e)}>事件传参3

  // 此时,就不需要返回一个函数了
  handleClick6 = (a, event) => {
    console.log(a);
    console.log(event)
  }

受控组件

受控组件,如其名,意思是组件的状态受react控制。在vue中,表单实现双向绑定时,是使用v-model来实现的,但在react中,并没有提供类似的api,所以需要自己实现:

  • 对于inputtextareaselect等都是通过控制value值来控制表单内容,通过onChange来监听表单输入
  • 对于radiocheckbox等是通过控制checked值来控制表单内容,通过onChange来监听表单输入
import React, { Component } from 'react';

class FormInput extends Component {
  constructor(props) {
    super(props)
    this.state = {
      name: '小花',
      info: 'xxxx',
      city: 'shenzhen',
      flag: true,
      gender: 'female',
      like: ['basketball']
    }
  }
  render() {
    let  { name, info, city, gender, like } = this.state
    // 表单受控组件
    return  

{name}


个人信息:{info}


城市:{city}


性别:{gender}


喜好:{like.map(item => { return {item} })}

篮球 足球
} inputChange = (e) => { this.setState({ name: e.target.value }) } textareaChange = (e) => { this.setState({ info: e.target.value }) } selectChange = (e) => { this.setState({ city: e.target.value }) } radioChange = (e) => { this.setState({ gender: e.target.value }) } checkboxChange = (e) => { const val = e.target.value let newLike = this.state.like.slice(); if(this.state.like.indexOf(val) !== -1) { let index = this.state.like.indexOf(val) newLike.splice(index, 1) }else { newLike.push(val) } this.setState({ like: newLike }) } } export default FormInput

父子组件间通信

vue类似,但父组件方法和属性都是通过属性方式传递(不同于vue的事件是通过v-on/@传递),子组件不是通过emit方式触发父组件方法,而是通过this.props.parentMethod方式来触发

// 父组件

  render() {
    return (
       
    )
  }
  handleDelete=(index) =>{
    // immutable概念,不允许在state上直接修改数据,否则后面性能优化可能存在问题
    let list = [...this.state.list]   // 拷贝一个副本,在此上面作修改
    list.splice(index,1)
    this.setState({
      list: list
    })
  }
// 子组件
import React, { Component } from 'react'

class TodoItem extends Component {
  constructor(props) {
    super(props);
    // 将this指向当前react组件,在构造函数时就指定this指向,有利于性能优化
    this.handleClick = this.handleClick.bind(this)
  }
  render() {
    return (
      // 不优化之前的写法是:
      //
) } handleClick() { // 触发父组件方法 this.props.deleteItem(this.props.index) } }

你可能感兴趣的:(react 基础知识整理)