React 基础知识

Hello,我是一名软件工程的大三学生,目前正在学习React,记录自己的学习过程,并以此督促自己学习。

目录

开发前的准备

React官网地址

Node版本依赖

创建项目环境命令(工具链)

项目结构

安装vsCode插件

JSX语法

React文件后缀

什么是JSX语法

解析JSX

元素渲染

条件渲染

列表渲染

组件

组件嵌套

快捷键:

Props

State

事件处理

添加样式

事件处理续

state可能是异步的

生命周期函数(钩子函数)

虚拟DOM:DOM渲染速度加速

表单之受控组件

表单之非受控组件

 组合

状态提升-组件之间的数据传递

状态提升


开发前的准备

React官网地址

https://zh-hans.reactjs.org/

Node版本依赖

Node >= 14.0.0 和 npm >= 5.6

创建项目环境命令(工具链)

npx create-react-app my-app()

注意:项目名称不能存在大写

项目结构

安装vsCode插件

ES7 React/Redux/GraphQL/React-Native snippets

JSX语法

React文件后缀

1. js
2. jsx

什么是JSX语法

就是JavaScript + XML(HTML)
<user>
    <name>iwen</name>
    <age>20</age>
</user>
严格:必须是闭合标签  <img />

解析JSX

遇到{}按照JS解析
遇到<>按照HTML解析

元素渲染

条件渲染

项目结构:把不需要的文件del 

1. 条件不同的选择
2. 渲染数据时候,如果数据不存在,可以用条件渲染进行判断
                                        Conditional.jsx
import React, { Component } from 'react'

export default class Conditional extends Component {
    render() {
        const message = {
            title:"some data"
        }
        let flag = true
        return (
            <div>
                {
                    flag ? '孙悟空' : '六耳猕猴'
                }
                {
                    message.title ? <p>{message.title}</p> : <p>have no data</p> 
                }
            </div>
        )
    }
}


                                         index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import Conditional from './Conditional'


const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <div>
    <Conditional />
  </div>
);

列表渲染

1. 渲染列表数据
2. key关键字
    每一条数据都需要一个唯一索引
        1. 使用index下标
        2. 使用每条数据的唯一id(推荐)
    key不发生变化,则不会重新渲染,key发生变化则需要重新渲染
                                    Lists.jsx
import React, { Component } from 'react'

export default class Lists extends Component {
    /**
     * render() 渲染函数:提供一个可渲染视图的方案
     */
    render() {
        const banners = {
            "success": true,
            "msg": "",
            "banner": [
                {
                    id: '1',
                    "title": "我在爱尔兰",
                    "content": "爱尔兰(爱尔兰语:Poblacht na hÉireann;英语:Republic of Ireland), 是一个西欧的议会共和制国家,西临大西洋,东靠爱尔兰海,与英国隔海相望,是北美通向欧洲的通道爱尔兰自然",
                },
                {
                    id: "2",
                    "title": "一个人的东京",
                    "content": "东京(Tokyo)是日本国的首都,是亚洲第一大城市,世界第二大城市。全球最大的经济中心之一。东京的著名观光景点有东京铁塔、皇居、国会议事堂、浅草寺、浜离宫、上野公园与动物园",
                },
                {
                    id: "3",
                    "title": "普罗旺斯的梦",
                    "content": "普罗旺斯(Provence)位于法国东南部,毗邻地中海和意大利,从地中海沿岸延伸到内陆的丘陵地区,中间有大河“Rhone”流过。自古就以靓丽的阳光和蔚蓝的天空,迷人的地中海和心醉",
                },
                {
                    id: "4",
                    "title": "相约夏威夷之夏",
                    "content": "夏威夷州位于北太平洋中,距离美国本土3,700公里,总面积16,633平方公里,属于太平洋沿岸地区。首府为檀香山。在1778至1898年间,夏威夷也被称为“三明治群岛”(Sandwich Islands)",
                }
            ]
        }
        return (
        /**
         * 如果是单行视图,可不不用增加()
         * 如果是多行视图,必须增加()
         */
            <div>
                <ul>
                    {
                        banners.banner.map((ele, index) => {
                            return (// 如果视图换行了,return后面要添加()
                                    // <div>必须只有一个根元素
                                    //     <li key={ele.id}>{ele.title}</li>
                                    //     <p>{ele.content}</p>
                                    // </div>
                                <li key={index}>
                                    <h3>{ele.title}</h3>
                                    <p>{ele.content}</p>
                                </li>
                            )
                        })
                    }
                </ul>
            </div>
        )
    }
}

组件

1. 后缀: .js  .jsx
2. 创建组件: 名字遵循大驼峰命名法
3. render函数中返回值需要添加()
4. render中返回的时候,只能存在一个根容器
5. 组件在应用的时候,首字母必须大写

组件嵌套

快捷键:

1. 快速生成一个React基本结构:rcc

Props

页面效果:

                                    MyProps.jsx
import React, { Component } from 'react'

export default class MyProps extends Component {
    render() {
        const { title, desc, list } = this.props
        return (
            <div>
                <h3>{title}</h3>
                <p>{desc}</p>
                <div>
                    {
                        list ? 
                        <ul>
                            {
                                list.map((ele,index)=>{
                                return <li key={index}>{ele}</li>
                                })
                            }
                        </ul>
                        : <p>暂无数据...</p>
                    }
                </div>
            </div>
        )
    }
}
                                    index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import MyProps from './MyProps'
const root = ReactDOM.createRoot(document.getElementById('root'));
let detatils = {
  title:"详情页",
  desc:"这是一个详情页描述",
  list:["测试1","测试2"]
}

let user = {
  title:"用户中心",
  desc:"这是一个用户中心"
}
root.render(
  <div>
    <MyProps title={detatils.title} desc={detatils.desc} list={detatils.list}/>
    <MyProps title={user.title} desc={user.desc}/>
  </div>
);

State

1. 修改State:this.setState({})
                                        MyState.jsx
import React, { Component } from 'react'

export default class MyState extends Component {
    /**
     * 定义State方式
     *  1. 简单方式(如果要修改状态的时候,可能出现错误)
     *  2. 构造函数方式(推荐)
     */

    // state = {
    //     userInfo:{
    //         name:"iwen",
    //         age:20
    //     }
    // }

    /**
     * props和state的区别
     *     props:对外
     *     state:对内
     * props和state是类似的,不同的是State是私有的,而且完全受控于当前组件
     */
    constructor(props) {
        super(props)
        this.state = {
            userInfo: {
                name: "@前端练习生",
                age: 20
            }
        }
    }
    render() {
        let userInfo = this.state.userInfo
        return (
            <div>
                <h3>state:状态</h3>
                <div>
                    <p>{userInfo.name}</p>
                    <p>{userInfo.age}</p>
                </div>
            </div>
        )
    }
}

事件处理

  1. React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

    1. React 事件的命名采用小驼峰式(camelCase),而不是纯小写。

    2. 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

  2. 事件是可以修改状态的

                                        Events.jsx
import React, { Component } from 'react'
import './style.css'
/**
 * .box{
    width: 150px;
    height: 150px;
    background: rgb(175, 175, 235);
}
 */
export default class Events extends Component {
    constructor(props){
        super(props)
        this.state = {
            flag :false
        }
        this.changeFlagHandle = this.changeFlagHandle.bind(this)//在构造函数里改变this,不会报错
    }
    mouseMoveHandle(){
        console.log('I LOVE REACT')
    }
    clickHandle(){
        console.log("我被点了")
    }
    // changeFlagHandle(){//报错了     Events.jsx:17 Uncaught TypeError: Cannot read properties of undefined (reading 'setState') 
    //     console.log(this)//undefined
    //     this.setState({
    //         flag:!this.state.flag
    //     })
    // }

    // changeFlagHandle =()=>{//使用箭头函数不会报错
    // this.setState({
    //     flag:!this.state.flag
    // })

    changeFlagHandle(){//在构造函数里改变this,不会报错
        this.setState({
            flag:!this.state.flag
        })
}
    render() {
        return (
            <div>
                <h3>事件处理</h3>
                <div className='box' onMouseMove={this.mouseMoveHandle}></div>
                <button onClick={this.clickHandle}>点我</button>
                <div>
                {/* <button onClick={this.changeFlagHandle.bind(this)}>ChangeFlag</button> */}
                <button onClick={this.changeFlagHandle}>ChangeFlag</button>
                    {
                        this.state.flag ? '孙悟空' : '六耳猕猴'
                    }
                </div>
            </div>
        )
    }
}

添加样式

1. class -> className

事件处理续

  1. 添加事件

  2. 研究一下关于改变State中的this问题

    1. 在视图结构中改变this指向:.bind(this)

    2. 将事件函数改成箭头函数(推荐)

    3. 在构造函数中,改变this指向:this.clickHandle = this.clickHandle.bind(this)

  3. 事件传递参数

  4. Event对象

                                    MyEvents.jsx
import React, { Component } from 'react'

export default class MyEvents extends Component {

    constructor(){
        super();
        this.state = {
            flag:false,
            names:["jack","jerry","tom"]
        }
        this.clickHandle = this.clickHandle.bind(this)
    }

    clickHandle(){
        this.setState({
            flag:!this.state.flag
        })
    }

    // clickHandle = () =>{
    //     this.setState({
    //         flag:!this.state.flag
    //     }) 
    // }

    clickListHandle = (ele,e) =>{
        console.log(ele);
        console.log(e);
    }

    baseClickHandle = (event) =>{
        console.log(event);
    }

    render() {
        // 解构赋值
        const { flag,names } = this.state;
        return (
            <div>
                <h3>事件处理续</h3>
                {/* <button onClick={ this.clickHandle.bind(this) }>按钮</button> */}
                <button onClick={ this.clickHandle }>按钮</button>
                {
                    flag ? "孙悟空" : "六耳猕猴"
                }
                <div>
                    <ul>
                        {
                            names.map((ele,index) =>{
                                return <li onClick={ (e) => this.clickListHandle(ele,e) } key={index}>{ ele }</li>
                            })
                        }
                    </ul>
                </div>
                <button onClick={ this.baseClickHandle }>按钮2</button>
            </div>
        )
    }
}

state可能是异步的

 从视图和下面的代码,可以看到一个打印0,一个视图呈现1,这可以简单的理解为是state的异步

                                      MySetState.jsx
import React, { Component } from 'react'

export default class MySetState extends Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
        this.clickHandle = this.clickHandle.bind(this)
    }
    clickHandle() {
        this.setState({
            count: this.state.count + 1
        })
        console.log(this.state.count)
    }
    render() {
        return (
            <div>
                <h3>State异步</h3>
                <button onClick={this.clickHandle}>changeCount</button>
                <p>{this.state.count}</p>
            </div>
        )
    }
}

 那我们怎么把它变成同步的呢


import React, { Component } from 'react'

export default class MySetState extends Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 0
        }
        this.clickHandle = this.clickHandle.bind(this)
    }
    // clickHandle() {
    //     this.setState({
    //         count: this.state.count + 1
    //     })
    //     console.log(this.state.count)
    // }
    async clickHandle() {
        await this.setStateAsync({
            count: this.state.count + 1
        })
        console.log(this.state.count)
    }
    setStateAsync(state){
        return new Promise((resolve)=>{
            this.setState(state,resolve)
        })
    }
    render() {
        return (
            <div>
                <h3>State异步</h3>
                <button onClick={this.clickHandle}>changeCount</button>
                <p>{this.state.count}</p>
            </div>
        )
    }
}

 

 从上面的代码中,我们可以知道,通过Promise,async await语法糖可以让state中代码变成同步的

生命周期函数(钩子函数)

官方文档:

挂在时:
    constructor:初始化(初始化状态,state)
    static getDerivedStateFromProps():虚拟DOM之后,实际DOM渲染之前
    render:创建虚拟DOM,进行Diff算法,更新DOM树都是再次进行的
    componentDidMount:组件渲染完成
更新时:
    static getDerivedStateFromProps():虚拟DOM之后,实际DOM渲染之前
    shouldComponentUpdate:返回值为Boolean类型,返回true,则允许DOM更新,返回false,则不允许DOM更新(优化)
    render:创建虚拟DOM,进行Diff算法,更新DOM树都是再次进行的
    getSnapshotBeforeUpdate(prevProps, prevState):更新前触发的钩子函数
    componentDidUpdate:组件更新完成
卸载时:
    componentWillUnMount:组件即将卸载

                                        LifeCycle.jsx
import React, { Component } from 'react'

export default class LifeCycle extends Component {
    constructor(props){
        super(props)
        console.log("构造函数:constructor");
        console.log(props.title);
        this.state = {
            count:0
        }
    }

    static getDerivedStateFromProps(props, state){
        console.log(props,state);//点了一下后的 {title: 'LifeCycle-fromParent测试参数'} {count: 1}
        console.log("渲染之前:getDerivedStateFromProps");
        return 10;
    }

    componentDidMount(){
        console.log("渲染完成:componentDidMount");
    }

    shouldComponentUpdate(nextProps, nextState){
        /**
         * this.props === nextProps;
         * this.state === nextState;
         */
        if(nextProps.count === this.state.count){
            return false;
        }
        console.log("是否允许渲染:shouldComponentUpdate");
        return true;
    }


    clickHandle = () =>{
        this.setState({
            count:this.state.count+1
        })
    }

    getSnapshotBeforeUpdate(prevProps, prevState){
        console.log(prevState);//点了一下后 这是之前第一次的值{count: 0}
        console.log("更新前:getSnapshotBeforeUpdate");
        return prevProps.title;//LifeCycle-fromParent测试参数
    }

    componentDidUpdate(prevProps, prevState, snapshot){
        console.log(snapshot);//LifeCycle-fromParent测试参数
        console.log("更新后:componentDidUpdate");
    }

    componentWillUnmount(){
        console.log("组件卸载:componentWillUnmount");
    }

    clickHandle=()=>{
        this.setState({
            count:this.state.count + 1
        })
    }
    render() {
        console.log("渲染中ing:render");
        return (
            <div>
                <h3>生命周期函数</h3>
                <button onClick={this.clickHandle}>增加</button>
                <p>{this.state.count}</p>
            </div>
        )
    }
}
                                        index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import LifeCycle from './LifeCycle'
const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <div>
    <LifeCycle title="LifeCycle-fromParent测试参数"/>
  </div>
);

虚拟DOM:DOM渲染速度加速

表单之受控组件

                                        MyForm.jsx
import React, { Component } from 'react'

export default class MyForm extends Component {
    constructor(props){
        super(props)
        this.state = {
            username:""
        }
    }
    changeHandle=(event)=>{
        this.setState({
            username:event.target.value
        })
    }
    getChangedataHandle=()=>{
        console.log(this.state.username)
    }
    render() {
        return (
            <div>
                <h3>受控组件</h3>
                <input type='text' value={this.state.username} onChange={this.changeHandle}/>
                <button onClick={this.getChangedataHandle}>get-change-data</button>
            </div>
        )
    }
}

 从上面的代码中,我们可以看出input里的vaule值是受到state管理的,但是如果有上百个input,我们就要为每其都绑定一个onChange事件,很麻烦,如何解决呢?

我们可不可以通过只绑定一个事件,来实现呢?如果绑定了同一个onChange事件,只要输入框输入一个值其他的input也相应的改变了,好像不可以......

此时,ES6站出来给我们解决了这个问题,Please See The Coding

                                        MyForm3.jsx
import React, { Component } from 'react'

export default class MyForm3 extends Component {
    constructor(props){
        super(props)
        this.state ={
            username:"",
            password:"",
            email:""
        }
    }
    changeHandle=(event)=>{
        this.setState({
            [event.target.name] : event.target.value
        })
    }
    getDataHandle=()=>{
        console.log(this.state)
    }
    render() {
        return (
            <div>
                <input type="text" name="username" onChange={this.changeHandle}/>
                <input type="text" name="password" onChange={this.changeHandle}/>
                <input type="text" name="email" onChange={this.changeHandle}/>
                <button onClick={this.getDataHandle}>get data</button>
            </div>
        )
    }
}

其实就是加了个name属性,关键代码:[event.target.name] : event.target.value

表单之非受控组件

                                        MyForm2.jsx
import React, { Component } from 'react'

export default class MyForm2 extends Component {
    constructor(props){
        super(props)
        this.username = React.createRef()
        this.password = React.createRef()
    }
    getDataHandle=()=>{
        // console.log(this.username)//{current: input}
        // console.log(this.username.current) //<input type="text">
        console.log(this.username.current.value) 
        console.log(this.password.current.value) 
    }
    render() {
        return (
            <div>
                <h3>非受控组件</h3>
                <input type="text" ref={this.username}/>
                <input type="text" ref={this.password}/>
                <button onClick={this.getDataHandle}>get data</button>
            </div>
        )
    }
}

 我们通过React.createRef 创建一个能够通过 ref 属性附加到 React 元素的 ref

 实际上就是获取元素节点

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.inputRef = React.createRef();  }

  render() {
    return <input type="text" ref={this.inputRef} />;  }

  componentDidMount() {
    this.inputRef.current.focus();  }
}

 组合

                                Composition.jsx
import React, { Component } from 'react'

export default class Composition extends Component {
    render() {
        return (
            <div>
                <h3>组合</h3>
                <div>
                    {this.props.children}
                </div>
            </div>
        )
    }
}
                                index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import Composition from './Composition'
const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <div>
    {/* 
      Vue:插槽
      React:组合
    */}
    <Composition>
      <p>我是组合内容-from index.js</p>
      <p>哈哈哈-from index.js</p>
    </Composition>
  </div>
);

状态提升-组件之间的数据传递

props: 父传子

                                        Child.jsx
import React, { Component } from 'react'

export default class Child extends Component {
    constructor(props){
        super(props)
        this.state ={
            msg:'from_child_data'
        }
    }
    clickHandle=()=>{//必须使用箭头函数 
        this.props.onEvent(this.state.msg)
    }
    render() {
        return (
            <div>
                <h3>Child</h3>
                <p>from parent title==>{this.props.title}</p>
                <button onClick={this.clickHandle}>return_callback_to_parent</button>
            </div>
        )
    }
}
                                        
                                        Parent.jsx
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
    constructor(props) {
        super(props)
        this.state = {
            msg: ""
        }
    }
    callbackHandle = (fromChild_data) => {
        this.setState({
            msg: fromChild_data
        })
    }
    render() {
        return (
            <div>
                <h3>Parent</h3>
                <Child title='Parent-title' onEvent={this.callbackHandle} />
                <p>from child data==>{this.state.msg}</p>
            </div>
        )
    }
}

状态提升

                                    Calculator.jsx
import React, { Component } from 'react'
import TemperatureInput from './TemperatureInput'
import BoilingVerdict from './BoilingVerdict'

/**
 * 接受一个华氏度参数,转换为摄氏度
 */
function toCelsius(fahrenheit) {
    return (fahrenheit - 32) * 5 / 9;
}

/**
 * 接受一个摄氏度,转换为华氏度
 */
function toFahrenheit(celsius) {
    return (celsius * 9 / 5) + 32;
}

// 温度转换

function tryConvert(temperature, convert) {
    // 字符串 -> 数字类型
    const input = parseFloat(temperature);
    // isNaN:ES6新特性
    if (Number.isNaN(input)) {
        return ""
    }
    const output = convert(input);
    // Math.round:获得一个整数(四舍五入)
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
}


export default class Calculator extends Component {

    constructor() {
        super();
        this.state = {
            temperature: "",
            scale: "c"
        }
    }

    handleCelsiusChange = (temperature) => {
        this.setState({
            scale: "c",  // 摄氏度
            temperature
        })
    }

    handleFahrenheitChange = (temperature) => {
        this.setState({
            scale: "f", // 华氏度
            temperature
        })
    }

    render() {
        const temperature = this.state.temperature
        const scale = this.state.scale
        const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature
        const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature
        return (
            <div>
                <TemperatureInput
                    scale="Celsius"
                    temperature={celsius}
                    onTemperatureChange={this.handleCelsiusChange}
                />
                <TemperatureInput
                    scale="Fahrenheit"
                    temperature={fahrenheit}
                    onTemperatureChange={this.handleFahrenheitChange}
                />
                <BoilingVerdict
                    celsius={parseFloat(celsius)}
                />
            </div>
        )
    }
}
                                    TemperatureInput.jsx
import React, { Component } from 'react'

export default class TemperatureInput extends Component {


    handleChange = (e) =>{
        this.props.onTemperatureChange(e.target.value)
    }

    render() {
        // scale:华氏度  摄氏度
        const scale = this.props.scale
        const temperature = this.props.temperature;
        return (
            <fieldset>
                <legend>Enter temperatur in { scale }</legend>
                <input type="text" value={ temperature } onChange={ this.handleChange }/>
            </fieldset>
        )
    }
}
                                        BoilingVerdict.jsx
import React, { Component } from 'react'

export default class BoilingVerdict extends Component {
    render() {
        // 接受一个参数:温度度数
        const celsius = this.props.celsius
        return (
            <div>
                {
                    celsius >= 100 ?  '达到了水的沸点' : "未达到水的沸点"
                }
            </div>
        )
    }
}

React基础知识已经完结,接下来继续写React高级部分,持续更新中......

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值