React快速上手(一):万字整理jsx基础语法

React作为当前全球流行的前端框架,是许多前端小伙伴在工作和学习中很难绕开的技术栈,学习React不仅是多掌握一个前端技能,同时由于React写法偏向原生的js语法,熟练使用React可以提升我们对于Javascript语法的掌握。

以下是笔者整理的jsx基础语法,文章内容较长,但描述方式和使用的代码示例相对浅显,易于阅读,欢迎您的阅读,阅读过程中如有发现错漏或疑惑,欢迎在评论区交流探讨!

一、jsx基础语法

1、jsx基础语法规则

(1)定义虚拟dom时,不要使用引号;

(2)标签中混入js表达式时要使用花括号{};

(3)样式类名指定不要使用class,使用className;

(4)内联样式,要使用style = {{ key: value }}的形式去写;

// 注意写在对象中要使用驼峰形式

{{ font-size: '16px' }} // bad
{{ fontSize: '16px' }} // good

(5)只能有一个根标签;

(6)标签必须闭合;

const vDom = <input value="text"> // bad
const vDom = <input value="text" /> // good

ReactDOM.render(vDom, document.getElementById('test');

(7)标签首字母

首字母小写,则将标签转为对应的html中的同名元素,若html中无该标签对应的同名元素,则报错;

首字母大写,则当成自定义组件(类似vue的子组件),react去渲染对应的组件,若组件没有定义,则报错;

(8){}的混合表达式为数组映射操作时,react会帮我们进行循环渲染;

const arr = ['a', 'b', 'c'];

const vDom = (
    <div>
        <h1>js列表测试</h1>
        <ul>{arr}</ul> // 会得到渲染结果:abc(需要添加其他效果,如<li>a</li>,需要对数组进行遍历映射操作)
    </div>
)

const vDom = (
    <div>
        <h1>js列表测试</h1>
        <ul>
            {
                arr.map((item, index) => {
                    return <li key={index}>{item}</li> // 遍历映射操作
                })
            }
        </ul>
    </div>
)
2、jsx表达式

(1)jsx的{}中可以使用js表达式,但是不能使用js语句

(2)js表达式与js语句的区别如下:

A. 表达式一个表达式会产生一个值,可以放在任何需要值的地方,例如:

(1). a // 变量
(2). a + b // 会产生值的运算表达式
(3). fun(1) // 调用函数(不管怎样函数都有默认return)
(4). arr.map() // 调用属性挂载的函数
(5). function fun() {} // 定义函数(即使没有return,也会默认返回函数本身)

B. 语句(代码块):不产生值,用于控制代码走向的,例如:

(1). if() {}
(2). for() {}
(3). switch() {case:xxxx}
(3)使用对比示例
const arr = ['a', 'b', 'c'];

// bad for语句没有输出,{}无法对结果进行语法转义
const vDom = (
    <div>
        <h1>js列表测试</h1>
        <ul>
            {
                for (let i = 0, len = arr.length; i < len; i ++) {
                    <li>{arr[i]}</li> // bad,在{}中不能直接写js语句
                }
            }
        </ul>
    </div>
)

// good 有输出,{}可以对结果进行语法转义
const vDom = (
    <div>
        <h1>js列表测试</h1>
        <ul>
            {
                arr.map((item, index) => {
                    // 跟vue类似,key是diff算法的比较标识,使用for循环渲染时不加上会报错!
                    return <li key={index}>{item}</li> // good
                })
            }
        </ul>
    </div>
)
3、类式组件

(1)react内置了一个类React.Component,写类式组件需要继承这个React.Component,没有特殊情况,可以直接继承它的构造器,不用另外写。

class MyComponent extends React.Component {
    // 重写render函数,用于定义组件内容
    render() {
        return <h2>类式组件</h2>
    }
}
ReactDOM.render(<MyComponent/>, document.getElementById('test');

(2)类式组件的渲染过程

ReactDOM.render(<MyComponent/>, document.getElementById('test');
/**
* 1、react解析组件标签,找到了MyComponent组件
* 2、发现组件是类定义的,随后new出该类的实例,并通过实例对象调用到原型上的render方法;
* 3、将render返回的虚拟DOM转化为真实DOM,随后呈现在页面中;
*/

(3)组件实例对象

(1)state状态,用来存储组件数据的,复杂组件才有state,简单组件(函数式组件)没有state

二、React类式组件三大基本属性

React类式的三大基本属性包括:state、props和refs,学会使用这三大属性的,就可以满足对类式组件的简单应用需求了。三大属性的基本作用如下:

(1)state —— 状态机,主要用于保存组件状态(存储需要实现响应式的数据)

(2)props —— 用于组件之间传递数据,组件内部props时只读状态的

(3)refs —— 该语法的能力与vue2中的refs基本一致,在标签上使用ref标识标签,React会在refs中统一收集全部被ref标识的标签

三、State详解

1、简介

state状态,用来存储组件数据的,复杂组件(类式组件)才有state,简单组件(函数式组件)没有state。

class Weather extends React.Component{
    constructor(props){
        super(props);
        this.state = { name: 'testname' }
    }
    render(){
        // this指向实例对象,state就是实例对象生成的,通过this.state即可获取到!
        return <h1>My name is {this.state.name}</h1>
    }
}
ReactDOM.render(<Weather/>, document.getElementById('test'));
2、state的简写方式

(1)类中的属性可以放在constructor之外定义

class Weather extends React.Component {
    constructor(props) {
        super(props);
        this.state = {isHot: false, wind: '微风'}
    }
    
    // 等同于:
    state = {isHot: false, wind: '微风'}
}

(2)并且,由于JS中箭头函数的指向是箭头函数定义位置的外层this指向,因此,在react中,类式组件中定义的箭头函数可以直接使用如下方式调用state:

建议:在react中,所有定义的函数都写成赋值语句形式 + 箭头函数(可以直接改变this指向)!

class Weather extends React.Component {
    constructor(props) {
        super(props);
    }
    
    state = {isHot: false, wind: '微风'}
    changeWeather = () => {
        const isHot = this.state.isHot;
        // 此时的this会指向Weather,而不用在constructor中写this.changeWeather = this.changeWeather.bind(this)
        this.setState({isHot: isHot});
    }
}
3、state总结

(1)state是组件最重要的属性,值是对象(key-value形式)

(2)组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(响应式)

(3)注意事项:

A. 组件中render的this为组件实例对象

B. 组件自定义的方法的this为undefined,解决方式:

  • 强制在constructor中使用bind()改变函数的this指向

  • 使用箭头函数

C. 状态数据state不能直接修改,需要通过setState函数进行修改

四、Props详解

1、简介

props是用来传递组件数据的,学过vue的小伙伴应该比较熟悉,react和vue中的props在用法以及作用上基本一致。

class Person extends React.Component {
    render() {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>    
            </ul>
        )
    }
}
// 调用Person组件并通过props传入数据
ReactDOM.render(<Person name="james"/>, document.getElementById('test'));
2、批量传递props

React提供了类似展开符...的语法糖,用于props的批量传递数据。在React的标签属性传递,可以直接使用...展开对象(React会将其转化为一个解构赋值的语法)。

class Person extends React.Component {
    render() {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>    
            </ul>
        )
    }
}
let p = {name: "testName", age: 18}

// 调用Person,批量传入name和age
ReactDOM.render(<Person {...p}/>, document.getElementById('test'));
4、对Props进行数据类型和默认值限制

为保证渲染的数据类型可控,在实际应用中通常需要对传入的props,避免意外的数据类型造成系统错误。

(1)历史溯源:在react15之前,使用React.propsType设置限制内容,即:限制内容的对象是挂在React上的;在React15之后将这个抽离了出来,成为了一个单独的包props-types.js

(2)对props进行类型限制 —— propsTypes

Person.propTypes = {
	name: PropTypes.string.isRequired, // string表示name必须为String类型,isRequired表示必传
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func // 限制speak类型为函数,由于function是一个关键字,因此React使用func表示function类型
}

(3)对props指定默认标签属性值 —— defaultProps

Persion.defaultProps = {
	sex: "未知",
	age: 18
}

(4)综合使用示例

class Person extends React.Component {
    render() {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>年龄:{this.props.age}</li>
                <li>性别:{this.props.sex}</li>
            </ul>
        )
    }
}

// 限制props内容
Person.propTypes = {
	name: PropTypes.string.isRequired, // string表示name必须为String类型,isRequired表示必传
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func // 限制speak类型为函数,由于function是一个关键字,因此React使用func表示function类型
}

// 设置props部分属性标签的默认值
Persion.defaultProps = {
	sex: "未知",
	age: 18
}

// p.sex不存在,传进props时会默认为"未知"
let p = {name: "testName", age: 18, speak: () => {}}
ReactDOM.render(<Person {...p}/>, document.getElementById('test'));
5、Props简写

(1)以上的写法是将props的限制propTypes和默认值defaultProps写在了组件类之外,其实也可以直接写在组件类之中,这样可以使组件类包裹的逻辑更加清晰!

在组件内部,写一个对象挂在组件类上,需要借助static关键字static声明的对象是直接挂在组件类上的(类似java的语法)

class Person extends React.Component {
    render() {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>年龄:{this.props.age}</li>
                <li>性别:{this.props.sex}</li>
            </ul>
        )
    }
    
    // 正确写法:使用static关键字,使其挂在组件类上
    static propTypes = {
        name: PropTypes.string.isRequired, // string表示name必须为String类型,isRequired表示必传
        sex: PropTypes.string,
        age: PropTypes.number,
        speak: PropTypes.func // 限制speak类型为函数,由于function是一个关键字,因此React使用func表示function类型
    }

    // 错误写法:这样是将propTypes定义在了组件实例上,而不是组件类上
    propTypes = {
        name: PropTypes.string.isRequired, // string表示name必须为String类型,isRequired表示必传
        sex: PropTypes.string,
        age: PropTypes.number,
        speak: PropTypes.func // 限制speak类型为函数,由于function是一个关键字,因此React使用func表示function类型
    }
    
    static defaultProps = {
        sex: "未知",
        age: 18
    }
}
class Person extends React.Component {
    render() {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>年龄:{this.props.age}</li>
                <li>性别:{this.props.sex}</li>
            </ul>
        )
    }
    
    // bad:这样是将propTypes定义在了组件实例上,而不是组件类上
    propTypes = {
        name: PropTypes.string.isRequired, // string表示name必须为String类型,isRequired表示必传
        sex: PropTypes.string,
        age: PropTypes.number,
        speak: PropTypes.func // 限制speak类型为函数,由于function是一个关键字,因此React使用func表示function类型
    }
}
6、propsconstructor的关系

propsconstructor的关系:如果写了constructor并且在constructor中需要使用this.props时,需要写上super(props)把props传进来,否则在构造器constructor中会出现this.props未定义(undefined)的问题:

class Person extends React.Component {
    constructor(props) {
        super(props);
        console.log(this.props); // 不添加super(props),则此处的this.props为undefined
    }
    
    
    render() {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>年龄:{this.props.age}</li>
                <li>性别:{this.props.sex}</li>
            </ul>
        )
    }
}
7、函数式组件中的props

props虽然是类式组件的三大基本属性,但是在函数式组件中同样可以使用props进行组件数据传递。

// 函数式组件
function Person(props) {
    return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>年龄:{this.props.age}</li>
                <li>性别:{this.props.sex}</li>
            </ul>
        )
}
// 直接将对于props的限制propTypes和默认值defaultProps挂在函数上即可

// 限制props内容
Person.propTypes = {
	name: PropTypes.string.isRequired, // string表示name必须为String类型,isRequired表示必传
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func // 限制speak类型为函数,由于function是一个关键字,因此React使用func表示function类型
}

// 设置props部分属性标签的默认值
Persion.defaultProps = {
	sex: "未知",
	age: 18
}

let p = {name: "testName", age: 18, speak: () => {}}
ReactDOM.render(<Person {...p}/>, document.getElementById('test'));
8、props总结

(1)每个组件对象都有props属性,组件标签的自定义属性都存在props中;

(2)作用:用于组件之间传递数据,组件内部props时只读状态的;

(3)组件内部读取props的方式:this.props.name;

(4)对props进行限制:

// react15.5之前,目前已弃用
Persion.propTypes = {
	name: React.PropTypes.string; // 直接在React对象上拿限制对象PropTypes(会使React对象变得臃肿)
}

// react新版本的方式,引入props-types.js包,其中就包含对象PropTypes
Persion.propTypes = {
	name: PropTypes.string;
}

(5)对props属性设置默认值:

Persion.defaultProps = {
	name: 'testName'
}

(6)扩展属性:将对象的所用属性通过...的方式传递给props

const obj = { name: "testname", age: 18 }
ReactDOM.render(<Person {...obj}/>, document.getElementById('test'));

(7)在构造函数constructor中使用this.props时,记得使用super(props)把props取出来

class Person extends React.Component {
    constructor(props) {
        super(props);
        console.log(this.props); // 不添加super(props),则此处的this.props为undefined
    }
}

五、Refs详解

1、简介

在标签上使用ref标识标签,React会在refs中统一收集全部被ref标识的标(该语法的能力与vue2中的refs基本一致

2、refs的三种使用形式

  • 字符串形式的ref(不被官网方推荐,接近废弃);

  • 回调函数形式的ref;

  • 使用createRef API(目前react最推荐的写法);

3、refs使用方式一(不推荐):字符串形式

原因:在React展示的讨论区连接中表明,过度使用字符串类型的ref可能会存在一些效率问题

使用示例如下:

class Person extends React.Component {
    showData = () => {
        const { value } = this.refs.inputId;
        alert(value);
    }

    render() {
        return (
            <div style={{ display: 'flex', alignItems: 'center' }}>
            	<p>输入框:</p>

                // 使用ref标记元素
                <input ref="inputId" style={{height: '20px'}} />
                <button onClick={this.showData} style={{marginLeft: '20px'}}>测试</button>
            </div>
        )
    }
}
ReactDOM.render(<Person />, document.getElementById('test'));
4、refs使用方式二:回调函数形式

回调函数形式的ref —— 回调函数的参数即当前的node节点,通过赋值将node节点放到组件实例上。React会自动调用ref上的回调函数

class Person extends React.Component {
    showData = () => {
        // 点击展示输入框中的内容
        const { input1 } = this;
        alert(input1.value);
    }

    render() {
        return (
            <div>
                // 使用回调函数的形式标记ref
                <input ref={currentNode => this.input1 = currentNode} />
                <button onClick={this.showData}>测试</button>
            </div>
        )
    }
}
ReactDOM.render(<Person />, document.getElementById('test'));

实现细节:回调函数形式的ref在更新的时候会被调用两次

原因:第一次回调函数的参数currentNode === null是为了有一个清空的初始化动作;即:React先赋一个null的值把原来标签的位置完全清空,再把一个新的标签内容写到那个位置

// render更新的时候,该回调函数被调用两次,第一次的currentNode===null,第二次的currentNode才是当前节点
<input ref={currentNode => this.input1 = currentNode} />

解决方法:不要用内联的形式写回调函数,用class定义的形式来写ref的回调函数(尽管这种区别在大多数情况下无关紧要)

class Person extends React.Component {
    showData = () => {
        // 点击展示输入框中的内容
        const { input1 } = this;
        alert(input1.value);
    }
    getInput = (currentNode) => {
        this.input1 = currentNode
    }
    render() {
        return (
            <div>
                {/*内联函数形式,会存在两次调用的问题*/}
                <input ref={currentNode => this.input1 = currentNode} />
                
                {/*class调用形式,不会出现更新时两次调用的问题*/}
                <input ref={this.getInput} />
                <button onClick={this.showData}>测试</button>
            </div>
        )
    }
}
ReactDOM.render(<Person />, document.getElementById('test'));
5、refs使用方式三(推荐):使用createRef API

createRef —— 调用后会返回一个容器,该容器可以存储被ref所标识的节点(这种写法有点类似vue3的函数式定义)

class Person extends React.Component {
    // 注意:createRef是“专人专用”的,一个createRef是同时放多个节点的话,后面的节点会覆盖前面的节点
    myRef = React.createRef();
    
    showData = () => {
        console.log(this.myRef.current); // 即可获得将ref设置为this.myRef的节点
    }
    render() {
        return (
            <div>                
                <input ref={this.myRef} />
                <button onClick={this.showData}>测试</button>
            </div>
        )
    }
}
ReactDOM.render(<Person />, document.getElementById('test'));
6、不要过度使用ref

有时候ref可以直接通过event.target代替,在标签中使用的函数没有定义入参时,react会在调用函数 时,将事件源event作为回调函数的入参来调用,此时可以通过event.target获取当前触发事件的节点

即:当发生事件的元素恰好是我们要操作的元素时,ref可以避免使用(通过event.target获取即可)

class Person extends React.Component {
    myRef = React.createRef();
    
    showData = (event) => {
        // 即可获得目标阶段的节点
        console.log(event.target); 
    }
    render() {
        return (
            <div>                
                <input ref={this.myRef} />
                <input onBlur={this.showData} />
            </div>
        )
    }
}
ReactDOM.render(<Person />, document.getElementById('test'));

结语

以上是笔者整理的React基础入门语法,阅读过程中如有发现错漏,欢迎在评论区回复纠正!如在使用React过程中遇到难点或有趣的使用方案,也欢迎在评论区交流探讨!

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值