喜迎
春节

React-知识点详解


前言: 什么是React?这里是React初识。

npx create-react-app “项目名”

react用到的第三方包

  • classnames
  • styled-components 将css独立来写的第三方包
  • prop-types:props的类型检测工具
  • axios
    在jsx里面写js代码就加一个{}

  • 创建一个简单的react:

import React from "react"
import ReactDOM from "react-dom"
// const app = <h1>welcome first!</h1> // jsx语法,不需要加引号
const createApp = (props) => {
    return (
        <div>
            {/* {只要在jsx里面写js代码就要加一层花括号注释也要用花括号} */}
            <h1>欢迎来到{props.title}</h1>
            <p>优秀的{props.title}</p>
        </div>
    )
}
const app = createApp({
    title: '德莱联盟'
})
ReactDOM.render(
    app,
    document.querySelector("#root")
)

react定义组件的方式:

  1. 箭头函数,组件的首字母大写
  2. 使用类组件,可以嵌套
  • <1> 箭头函数方法:
// react创建组件的第一个方式:箭头函数,这个名字要大写 App
const App = (props) => {
    return (
        <div>
            <h1 title={props.title}>想吃很多{props.title}</h1>
            <p>有很多{props.title}</p>
        </div>
    )
}
ReactDOM.render(
    <App title="炸鸡腿"/>,
    document.querySelector("#root")
)
  • <2>使用类继承React.Component:
// 定义组件的第二个方法:使用类继承React.Component
import React, { Component } from "react"
import { render } from "react-dom"

class App extends Component {
    render () {
        console.log(this.props) //{title: "类组件是继承React.Component的"}  参数传递就用this.props
        return (
            <div>
                <h1>类组件</h1>
                <p>{this.props.title}</p>
            </div>
        )
    }
}
// render 是ReactDOM提供的方法,这个方法通常只会使用一次
render(
    <App title="类组件是继承React.Component的"/>,
    document.querySelector("#root")
)

// 16版本以前创建组件的方法
// React.createClass({
//     render () {
//         return (
//             <div>{this.props.title}</div>
//         )
//     }
// })

组件嵌套

import React, { Component } from "react"
import { render } from "react-dom"

const Header = () => <h1>组件嵌套</h1>
class App extends Component {
    render () {
        return (
            <div>
                <Header />
                <p>{this.props.title}</p>
            </div>
        )
    }
}

render(
    <App title="react组件可以嵌套"/>,
    document.querySelector("#root")
)
  • jsx原理,通过React.createElement的方式创建元素,有无限个参数,但前两个是固定的,第一个标签名,第二个是标签的属性,剩下的都是子元素
    React.createElement(type,[props],[…children])
import React, { Component } from "react"
import { render } from "react-dom"
class App extends Component {
    render () {
        return (
            React.createElement (
                'div',
                {
                    className: 'app',
                    id: 'appRoot'
                },
                React.createElement (
                    'h1',
                    {
                        className: 'title'
                    },
                    'jsx原理'
                ),
                React.createElement (
                    'p',
                    null,
                    'jax到底怎么写?'
                )
            )
        )
    }
}

// const appVDom = {
//     tag: "div",
//     attrs: {
//         className: "app",
//         id: "appRoot"
//     },
//     children: [
//         {
//             tag: "h1",
//             attrs: {
//                 className: "app"
//             },
//             children: [
//                 "jsx原理"
//             ]
//         }, {
//             tag: "p",
//             attrs: null,
//             children: [
//                 "jsx到底怎么写呢?"
//             ]
//         }
//     ]

// }

render(
    <App />,
    document.querySelector("#root")
)

react里的css

  1. 使用style标签内联创建
import React, { Component } from "react"
import { render } from "react-dom"
import classNames from "classnames"
import styled from "styled-components"
import './index.css'

const Title = styled.h2`
    color: #f00
    `

class App extends Component {
    render () {
        const style = {color: '#f00'}
        return (
            <div>
                <h1>元素的样式</h1>
                <Title>styled-components的使用</Title>
                <ol>
                    <li style={style}>使用style方式</li>
                    <li className='redclass'>使用class方式,但在react里要写成className</li>
                    <li className={classNames('a',{'b': true,'c': false})}>要动态添加className使用classnames第三方包</li>// 该li的class只有a,b没有c
                </ol>
            </div>
        )
    }
}

render(
    <App />,
    document.querySelector("#root")
)
  1. 使用class方式,但是要写成className,样式写在css文件中,所有要先import引入
    第三方的css包:1、classnames,可以动态添加不同的className

  2. styled-components 将css独立来写的第三方包

React项目组件化

快捷键:rcc react的class component;rfc react的function component

  1. 整个单页应用的入口文件:App.js

  2. 入口文件:index.js

  3. 其余的子组件包在App下面

  4. 在src下新建一个components目录,里面包含所有的子组件,一个子组件就是一个文件夹,用组件名作为文件夹名字;同时,在components的根目录下还有一个index.js文件,用来引入和导出所有的子组件,在App.js里面引入所有的组件。

  1. 在src下创建一个services文件夹,里面有apis.js和index.js文件。apis.js是统一管理接口的文件
组件化开发React todolist, 项目开发中的组件的基本目录结构基本上是这样的:
> /your-project
>
> - src
>   - …
>   - components
>     - YourComponentOne
>       - index.js/YourComponentOne.js
>     - YourComponentTwo
>       - index.js/YourComponentTwo.js
>     - index.js 用于导出组件
注意:一个组件只干一件事情 ,所以TodoList和TodoItem要做成两个组件,这样也方便于后期理解shouldComponentUpdate
  • 一个组件里的return只能有一个根元素 ,因此react里可以使用Fragment(需要import引入,是一个空标签),或者直接使用空标签
import React, { Component, Fragment } from 'react'
import {
    TodoHeader,
    TodoInput,
    TodoList
} from './components'
export default class App extends Component {
  render() {
    return (
      <Fragment>
        <TodoHeader />
        <TodoInput />
        <TodoList />
      </Fragment>

    //   <>
    //     <TodoHeader />
    //     <TodoInput />
    //     <TodoList />
    //   </>
    )
  }
}

两种导出方式:

//第一种,当需要对这里引入的组件进行处理再导出时使用
// import TodoHeader from './TodoHeader'
// import TodoInput from './TodoInput'
// import TodoList from './TodoList'

// export {
//     TodoHeader,
//     TodoInput,
//     TodoList
// }

//第二种
export { default as TodoHeader} from './TodoHeader'
export { default as TodoInput} from './TodoInput'
export { default as TodoList} from './TodoList'

组件的数据挂载方式

  1. 通过props传递
    function组件直接通过props.xxx,class组件通过this.props.xxx传递
import React from 'react'

export default function TodoHeader(props) {
  console.log(props)// {title: "待办事项"}
  return (
    <h1>
      {props.title}
    </h1>
  )
}

属性(props)

props.children 组件标签内的内容

// App.js
export default class App extends Component {
  render() {
    return (
      <Fragment>
        <TodoHeader desc="完成今天所有的事">
          待办事项列表
        </TodoHeader>
        <TodoInput btnText="ADD"/>
        <TodoList />
      </Fragment>

// TodoHeader/index.js
export default function TodoHeader(props) {
  console.log(props) // {desc: "完成今天所有的事", children: "待办事项列表"}
  return (
    <h1>
      {props.children}
    </h1>
  )
}
  • prop-types:props的类型检测工具
// TodoHeader/index.js
import React from 'react'
import PropTypes from 'prop-types'
export default function TodoHeader(props) {
  console.log(props)
  return (
    <>
      <h1>
        {props.children}
      </h1>
      <h3>{props.desc}</h3>
      <p>{props.x + props.y}</p>
    </>
  )
}
TodoHeader.propTypes = {
  desc: PropTypes.string,
  x: PropTypes.number.isRequired,
  y: PropTypes.number.isRequired
}
  • class组件写法:
// TodoInput.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
  static propTypes = {
    btnText: PropTypes.string
  }
  static defaultProps = {
    btnText: '添加TODO' //组件默认值
  }
//TodoHeader.defaultProps = {
//  desc: '如果能重来'
//}  //funciton组件写法
  render() {
    return (
      <div>
        <input type="text"/><button>{this.props.btnText}</button>
      </div>
    )
  }
}

state

组件内部状态定义用state,有两种定义方法 ,而props是外部传入的属性.只有class组件才有this和state

// App.js
export default class App extends Component {
// 第一张定义方式
//  state = {
//    title: '待办事项'
//  }
// 第二种定义方法
  constructor () {
    super()
    this.state = {
      title: '待办事项',
      desc: '完成今天所有的事'
    }
  }
  render() {
    return (
      <Fragment>
        <TodoHeader desc={this.state.desc}>
          {this.state.title}
        </TodoHeader>

组件的分类:

  • class组件和function组件
  • 受控组件、不受控组件、半受控组件(通过props传入属性,在组件内部没法修改的叫受控组件,通过state定义的状态不是外部传入的时不受控组件)
  • 不能跨组件传递props,必须一层一层的传
  • 组件模板渲染语法
  {/* {this.todos[0].isCompleted && '完成'} */}
  {this.state.todos[0].isCompleted ? '完成' : '未完成'}
          {
            this.state.todos.map(todo => {
              return <div key={todo.id}>{todo.title}</div>
            })
          }

  {div dangerouslySetInnerHTML={{__html: this.state.article}}} // 渲染不带HTML标签的内容

dangerouslySetInnerHTML,类似于vue的v-html,输出不带HTML标签的内容

属性vs状态

  • 相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)

  • 不同点:

1.属性能从父组件获取,状态不能
2.属性可以由父组件修改,状态不能
3.属性能在内部设置默认值,状态也可以
4.属性不在组件内部修改,状态要改
5.属性能设置子组件初始值,状态不可以
6.属性可以修改子组件的值,状态不可以

  • state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内1部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState方法进行更新,setState 会导致组件的重新渲染。

  • props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。

  • 如果搞不清 state 和 props 的使用场景,记住一个简单的规则:尽量少地用 state,多用 props。

  • 没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。

更改状态(setState)

  • react不能直接通过this.state.xxx = !this.state.xxx来直接更改状态,要用setSate()方法来更改。(能修改数据,但是页面不会渲染)
  • setState()有两个参数,第一个参数有两种情况:第一种是一个对象;第二种情况是一个方法。
  • setState的第二个参数是一个回调在里面return一个对象,由于setState是异步的,想要获取最新的state要在第二个参数的回调里获取
  • react的setState是异步的,setState里的方法要比它外面的方法后执行
import React, { Component } from 'react'

export default class Like extends Component {
    constructor () {
        super()
        this.state = {
            isLiked: false
        }
    }
    likedClick = () => {
        // setState的第一种写法
        // this.setState({
        //     isLiked: !this.state.isLiked
        // })
        // 第二种写法是一个方法,可以直接传上次的状态prevState和props
        this.setState((prevState) => {
            console.log(prevState)
            return {
                isLiked: !prevState.isLiked
            }
        }, () => {
            // setState是异步的,想要获取最新的state要在这个回调里获取
            console.log(this.state)
        })
    }
  render() {
    return (
      <div>
        <span onClick={this.likedClick}>
            {
                this.state.isLiked ? '太菜了!👍' : '棒棒哒!👎'
            }
        </span>
      </div>
    )
  }
}

react事件

如果要修改state里的值需要使用onChange事件(这是采用驼峰命名的),然后通过setState方法来修改值,否则页面上不能输入

// TodoInput/index.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
  static propTypes = {
    btnText: PropTypes.string
  }
  static defaultProps = {
    btnText: '添加TODO'
  }
  constructor () {
    super()
    this.state = {
      inputValue: 'xxx'
    }
    // this.addTodo = this.addTodo.bind(this)
  }
  onInputChange = (e) => {
    this.setState({
      inputValue: e.currentTarget.value
    })
  }
addTodo = (id) => {
  console.log(this.state,id)
  }
  render() {
    return (
      <div>
        <input type="text" onChange={this.onInputChange} value={this.state.inputValue}/>
        <button onClick={this.addTodo.bind(this,1234)}>{this.props.btnText}</button>
      </div>
    )
  }
}
// App.js
addItem = (todoTitle) => {
    console.log(todoTitle)
    // this.setState({
    //   // 添加todo的第一个方法,这里不能用push,push返回的是数组的长度
    //   todos: this.state.todos.concat({
    //     id: Math.random(),
    //     title: todoTitle,
    //     isCompleted: false
    //   })
    // })
    // 第二个方法,先把数组复制一份
    // const newTodos = this.state.todos.slice()
      const newTodos = [...this.state.todos]
    newTodos.push({
        id: Math.random(),
        title: todoTitle,
        isCompleted: false
    })
    this.setState({
      todos: newTodos
    })
  }

react里面通过ref获取组件或者dom元素,在使用ref之前要先调用React.createRef来创建ref,要在constructor里创建ref。

import React, { Component, createRef } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
  static propTypes = {
    btnText: PropTypes.string
  }
  static defaultProps = {
    btnText: '添加TODO'
  }
  constructor () {
    super()
    this.state = {
      inputValue: ''
    }
    // this.addTodo = this.addTodo.bind(this)
    // 在constructor里创建ref
    this.inputDom = createRef()
  }
  onInputChange = (e) => {
    this.setState({
      inputValue: e.currentTarget.value
    })
  }
  addKeyUp = (e) => {
    if(e.keyCode === 13){
      // console.log(e)
      this.addTodo()
    }
  }
  addTodo = () => {
    if(this.state.inputValue === ''){
      return
    }
    console.log(this.inputDom)
    this.props.addItem(this.state.inputValue)
    this.setState({
      inputValue: ''
    }, () => {
        this.inputDom.current.focus()
    })
  }
  render() {
    return (
      <div>
        <input type="text" onChange={this.onInputChange} onKeyUp={this.addKeyUp} value={this.state.inputValue} ref={this.inputDom}/>
        <button onClick={this.addTodo}>{this.props.btnText}</button>
      </div>
    )
  }
}

文章作者: NekoDeng
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 NekoDeng !
评 论
 上一篇
JS-三个高阶函数filter-map-reduce
JS-三个高阶函数filter-map-reduce
前言: JS-三个高阶函数filter-map-reduce,需要掌握很好用。 filter filter 有过滤的意思filter(参数) 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。filter中的参数
2020-10-02
下一篇 
关于在Vue项目初始化echarts时报错TypeError:Cannot read property ‘init‘ of undefined的解决办法
关于在Vue项目初始化echarts时报错TypeError:Cannot read property ‘init‘ of undefined的解决办法
前言:在Vue项目中遇到的一个问题,初始化echarts时报错TypeError:Cannot read property ‘init‘ of undefined的解决办法 导入echarts 在Vue项目中的main.js文件中导入如下
2020-09-30
  目录