美文网首页React
react 基础知识整理

react 基础知识整理

作者: miao8862 | 来源:发表于2021-06-16 21:39 被阅读0次

最近开始复习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 (
      <Fragment>
        <div>
          <input type="text"/>
          <button>提交</button>
        </div>
        <ul>
          <li>学英语</li>
          <li>学医学</li>
        </ul>
      </Fragment>
    )
  }
}

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

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

// 不能写小写
// ReactDOM.render(<todolist />, 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 = "<mark>这是html内容</mark>"
    const htmlObj = { __html: htmlContent }
    // 返回一个标签时,则不用()包裹
    return <p dangerouslySetInnerHTML={htmlObj}></p>
    // return (
    //   // 多行内容return返回时用()包裹,且最外层只能有一个容器
    //   // Fragment相当于vue中的template作用
    //   <Fragment>
    //     <p>多行内容</p>
    //     <p dangerouslySetInnerHTML={htmlObj}></p>
    //   </Fragment>
    // )
  }
}

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`?

<input className="input" />

注释

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

使用不转义的html

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

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

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

export default HtmlCotent;

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

render() {
  return (
    <Fragment>
      <div>
        {/* 使用bind改变this指向 */}
        {/* 想实现点击label时,聚焦在input框上,使用htmlFor实现 */}
        <label htmlFor="inputContent">输入内容:</label>
        <input type="text"
          id="inputContent"
          className="input"
          value={this.state.inputValue}
          onChange={ this.handleChange.bind(this) }
        />
        <button onClick={this.handleClick.bind(this)}>提交</button>

      </div>
    </Fragment>
  )
}

条件渲染

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 <p>hello</p>
    }else {
      return <p>byebye</p>
    }

    // 三元表达式
    // return (this.state.show ? <p>hello</p> : <p>byebye</p>)

    // 与运算&&,或运算||
    // return this.state.show && <p>hello</p>
    // return this.state.show || <p>byebye</p>
  }
}

export default Condition

suspense

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

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

export default SuspenseDemo;

循环渲染

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

    // 循环渲染
    return (
      <ul>
        {this.state.list.map(item => 
          {
            return <li key={item.id}>{item.age}</li>
          }  
        )}
      </ul>
    )

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 (
      <div>
        <p>{this.state.count}</p>
        <button onClick={this.addCount}>累加</button>
      </div>
    )
  }
}

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 <p onClick={this.handleClick.bind(this)}>{this.state.name}</p>

    // 要使用onXxxx的写法绑定事件
    
    return (
      <div>
        {/* 第一种写法 */}
        <p onClick={this.handleClick}>{this.state.name}</p>
        {/* 第二种写法 */}
        <p onClick={this.handleClick2}>{this.state.name}</p>
      </div>
    )
  }
  // 第一种定义方法
  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 (
      <div>
        {/* react事件性质 */}
        <a href="http://www.baidu111.com" onClick={this.handleClick3}>跳转链接</a>
      </div>
    )
  }
  // 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); 

    // 原生事件的对象,<a href="http://www.baidu111.com">跳转链接</a>
    console.log('nativeEvent target:', event.nativeEvent.target)  

    // 事件挂载的对象,react16前是挂载到document上的,react17后是挂载到root元素
    console.log('nativeEvent currentTarget:', event.nativeEvent.currentTarget ) 
  }
image.png
  • 事件的传参
  1. 使用bind传参,自定义参数从第二个开始传
<p onClick={this.handleClick4.bind(this, 'aa', 'bb')}>事件传参一</p>
// event是默认添加到最后一个形参中的
  handleClick4(a, b, event) {
    console.log('参数:', a, b, event);  // 参数: aa bb SyntheticBaseEvent
  }
  1. 使用箭头函数,event需要定义在返回的函数中
<p onClick={this.handleClick5('aa')}>事件传参2</p>
// 需要返回一个函数,event作为返回函数的参数
  handleClick5 = (a) => {
    return (event) => {
      console.log('a', a);  // aa
      console.log('e', event);  // SyntheticBaseEvent
    }
  }
  1. 在绑定时,使用箭头函数,将event参数传入,推荐使用这种,写法方便简洁一些
<p onClick={(e) => this.handleClick6('aa', e)}>事件传参3</p>
  // 此时,就不需要返回一个函数了
  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  <div>
        <p>{name}</p>
        <label htmlFor="inputName">姓名:</label>
        <input type="text" id="inputName" value={name} onChange={this.inputChange}/>
        <hr/>
        <p>个人信息:<span>{info}</span></p>
        <textarea value={info} onChange={this.textareaChange}></textarea>
        <hr/>
        <p>城市:<span>{city}</span></p>
        <select value={city} onChange={this.selectChange}>
          <option value="beijing">北京</option>
          <option value="shenzhen">深圳</option>
          <option value="shangehai">上海</option>
        </select>
        <hr/>
        <p>性别:<span>{gender}</span></p>
        男 <input type="radio" name="gender" value="male" checked={gender === 'male'} onChange={this.radioChange}/>
        女 <input type="radio" name="gender" value="female" checked={gender === 'female'} onChange={this.radioChange}/>
        <hr/>
        <p>喜好:{like.map(item => {
          return <span>{item}</span> 
        })}</p>
        <input type="checkbox" value="basketball" checked={like.includes('basketball')} onChange={this.checkboxChange}/>篮球
        <input type="checkbox" value="football" checked={like.includes('football')} onChange={this.checkboxChange}/>足球
      </div>
    
  }
  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 (
       <TodoItem 
          content={item} 
          index={index} 
          deleteItem={this.handleDelete.bind(this)}
        />
    )
  }
  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 (
      // 不优化之前的写法是:
      //<div onClick={this.handleClick.bind(this)}></div>
      <div onClick={this.handleClick}></div>
    )
  }
  handleClick() {
    // 触发父组件方法 
    this.props.deleteItem(this.props.index)
  }
}

相关文章

网友评论

    本文标题:react 基础知识整理

    本文链接:https://www.haomeiwen.com/subject/luwzeltx.html