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 (
      <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)
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容