React从入门到精通系列之(12)深入理解JSX

十二、深入理解JSX

从根本上讲,JSX就是提供了一个React.createElement(component,props,...children)函数的语法糖。就像下面的JSX代码

<MyButton color="blue" shadow={2}>
    Click Me
</MyButton>

经过编译后为:

React.createElement(
    MyButton,{color: 'blue',shadow: 2},'Click Me'
)

如果一个标签没有子元素的话,你可以使用/>自动闭合。例如:

<div className="sidebar" />

经过编译后为:

React.createElement(
    'div',{className: 'sidebar'},null
)

如果你想测试一些特定的JSX是如何转换成JavaScript的话,你可以试试在线Babel编译器

指定React元素类型

JSX标记的第一部分决定了React元素的类型。

首字母大写的类型表示JSX标记指的为React组件。 这些标签被编译为对指定变量的直接引用,因此如果使用JSX <Foo />表达式,Foo必须在当前的作用域内。

React必须在作用域内

由于JSX编译的本质是对React.createElement调用,因此React库也必须始终在JSX代码的作用域中。
例如,虽然CustomButton没有直接引用React,但是这两个导入的模块在这代码中也还是很有必要的:

import React from 'react';
import ReactDOM from 'react-dom';

function WarningButton(props) {
    // return React.createElement(CustomButton,{color: 'red'},null);
    return <CustomButton color="red" />
}

如果不使用JavaScript打包工具并将React通过script标签引入,那么它就会作为一个全局变量React

对JSX类型使用『点』表示符

您还可以使用JSX中的点表示符来引用React组件。 如果您有一个模块会导出很多React组件的话,使用这种方法就会十分方便。 例如,如果MyComponents.DatePicker一个组件,您可以直接从JSX使用它:

import React from 'react';
import ReactDOM from 'react-dom';

const MyComponents = {
    DatePicker(props) {
        return <div>这里有一个颜色为{props.color}的日期选择器</div>
    }
};

function BlueDataPicker(props) {
    return <MyComponents.DatePicker color="blue" />
}

ReactDOM.render(
    <BlueDataPicker />,document.getElementById('root')
);

用户自定义组件必须是首字母大写

当元素类型以是小写字母开头时,它指向一个内置组件,如<div><span>,并生成一个字符串'div''span'传递给React.createElement。 以大写字母开头的类型,如<Foo />编译为React.createElement(Foo),并且在当前作用域内寻找这个名称Foo的已定义或已导入组件。

我们建议使用首字母大写命名组件。 如果你有一个以小写字母开头的组件,请在JSX中使用它之前请将它赋值给一个首字母大写的变量。

下面代码不会按预期运行:

import React from 'react';

//这是错误的,这个组件应该为首字母大写
function hello(props) {
    // 这是正确的,因为div是一个有效的html标签
    return <div>Hello {props.name}</div>;
}

function HelloWorld(props) {
    // 这是错误的,因为它是首字母小写,所以React认为<hello />是一个html标签
    return <hello name="zhangyatao" />
}

想要修复上面的问题,我们必须将hello重命名Hello,通过<Hello />来使用该组件:

import React from 'react';

// 这是正确的
function Hello(props) {
    return <div>Hello {props.name}</div>;
}

function HelloWorld(props) {
    // 这是正确的
    return <Hello name="zhangyatao" />;
}

在运行的时候选择组件类型

不能将常规的javascript表达式用作React元素类型。 如果你想使用一个通用表达式来表示元素的类型,只需将它赋值给一个首字母大写的变量即可。
这通常出现在当你想基于同一个props渲染一个不同的组件的情况下:

import React from 'react';
import {Com1,Com2} from './Components';

const components = {
    myCom1: Com1,myCom2: Com2
}

function runcom(props) {
    // 这是错误的,JSX的类型不能这么写
    return <components[props.comType] type={props.type} />;
}

想要解决上面的问题,只需要将它们赋值给一个首字母大写的变量即可:

import React from 'react';
import {Com1,Com2} from './Components';


const components = {
    myCom1: Com1,myCom2: Com2
}

function runcom(props) {
    // 这是正确的,将它们赋值给一个首字母大写的变量
    const MyCom = components[props.comType];
    return <MyCom type={props.type} />;
}

JSX中的Props

在JSX中指定Props有以下几种不同的方法

JavaScript表达式

你可以传递任何JavaScript表达式作为Props,用{}括住它们就可以使用。 例如,在这个JSX中:

<MyComponents foo={1 + 2 + 3 + 4} />

对于MyComponent来说,props.foo的值将为10,因为是通过表达式1 + 2 + 3 + 4计算得到的。

if语句和for循环在JavaScript中不是表达式,因此它们不能在JSX中直接使用。 相反,写完它们之后你可以把JSX放在里面。 例如:

function NumberDescriber(props) {
    let description;
    if (props.number % 2 === 0) {
        description = <strong>偶数</strong>
    } else {
        description = <strong>奇数</strong>
    }
    return <div>{props.number}是一个{description}.</div>;
}

字符串直接量

你可以传递一个字符串内容作为props。 这两个JSX表达式是等价的:

<MyComponent message="hi zhangyatao" />

<MyComponent message={'hi zhangyatao'} />

当你传递一个字符串直接量时,它的值是经过html转义的。 所以这两个JSX表达式是等价的:

<MyComponent message='&lt;3' />

<MyComponent message={'<3'} />

Props认值为true

如果你没有给Props传入一个值,那么它的认值为true,这两个JSX表达式是等价的:

<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

一般来说,我们不建议使用它,因为它可以使用ES6对象的简写{foo},也就是{foo:foo}的简称会和{foo:true}混淆。 这种行为在这里只是方便它匹配到HTML行为。

Props传递

如果你有一个对象类似的数据作为props,并且想在JSX中传递它,你可以使用...作为一个“spread”运算符传递整个props对象。 这两个组件是等效的:

function App() {
    return <Greeting firstName="yatao" lastName="zhang" />;
}

function App() {
    const props = {firstName: 'yatao',lastName: 'zhang'};
    return <Greeting {...props} />;
}

当创建一个通用容器时,spread props很有用。
然而,他们也可以让你的代码变得有点凌乱,这样很容易使大量不相关的prps传递给那些不关心它们的组件。 建议您谨慎使用此语法。

JSX中的子元素和子组件

在包含开始标记和结束标记的JSX表达式中,这些标记间的内容通过一种特殊的prop:props.children传递。 有几种不同的方式传递子组件:

字符串直接量

你可以在开始和结束标签之间放一个字符串,那么props.children就是那个字符串。 这对许多内置的HTML元素很有用。 例如:

function MyComponent(props) {
    return <div>{props.children}<div>; //=> <div>hello zhangyatao</div>
}

<MyComponent>Hello zhangyatao</MyComponent>

这是有效的JSX,并且MyComponent中的props.children将是字符串“Hello zhangyatao”。 HTML标签是不会经过转义的,所以你一般可以写JSX就像你写HTML一样:

<div>这是一个html标签 &amp; 同时也是个JSX</div>

JSX会删除行的开始和结尾处的空格。 它也会删除间的空行。 与标签相邻的空行被会被删除;
在字符串文本中间出现的空行会缩合成一个空格。 所以这些都渲染相同的事情:

<div>hello zhangyatao</div>

<div>
    hello zhangyatao
</div>

<div>
    hello
    zhangyatao
</div>

<div>

hello zhangyatao
</div>

JSX子元素

你可以使用很多个JSX元素作为子元素。 这对需要嵌套的显示类型组件很有用:

<Dialog>
    <DialogHeader />
    <DialogBody />
    <DialogFooter />
</Dialog>

你可以将不同类型的子元素混合在一起,因此JSX子元素可以与字符串直接量一起使用。 这是JSX的另一种方式,就像一个HTML一样:

<div>
    这是一个列表
    <ul>
        <li>item 1</li>
        <li>item 2</li>
    </ul>
</div>

一个React组件不可能返回多个React元素,但是一个JSX表达式可以包含多个子元素,因此如果你想让一个组件渲染多个东西,你可以将它们统一放置在就像上面那样的div中。

Javascript表达式

您可以将任何JavaScript表达式放在{}中作为子组件传递。 例如,下面这些表达式是等价的:

function MyComponent(props) {
    return <div>{props.children}<div>; //=> <div>hi zhangyatao</div>
}

<MyComponent>hi zhangyatao</MyComponent>

<MyComponent>{'hi zhangyatao'}</MyComponent>

这通常用于渲染任意长度的JSX表达式列表。 例如,这将渲染一个HTML列表:

function Item(props) {
    return <li>{props.message}</li>;
}

function TodoList(props) {
    const todos = ['完成文档','出去逛街','打一局dota'];
    return (
        <ul>
            {todos.map(message => <Item key={message} message={message} />)}
        </ul>
    );
}

JavaScript表达式可以与其他类型的子元素混合使用。 这通常用于替换字符串模板:

function Hello(props) {
    return <div>Hello {props.name}</div>;
}

使用函数作为子元素

通常,插入JSX中的JavaScript表达式都最终返回为一个字符串、React元素、一个列表。

当然,props.children可以像任何其他props那样工作,它可以传递任何类型的数据,并不局限于那些告诉React应该如何渲染的小编。 例如,如果您有一个自定义组件,您可以将props.children作为一个回调函数

import React from 'react';
import ReactDOM from 'react-dom';

function Repeat(props) {
    let items = [];
    let callback = props.children;
    var numTimes = props.numTimes;
    for(var i = 0 ; i < numTimes ; i++ ){
        items.push(callback(i));
    }
    return <div>{items}</div>;
}

function listofTenThings(props) {
    return (
        <Repeat numTimes={10}>
            {index => <div key={index}>这是列表中的第{index}项</div>}
        </Repeat>
    );
}
ReactDOM.render(
    <listofTenThings/>,document.getElementById('root')
);

传递给自定义组件的子元素可以是任何东西,只要在React在渲染之前,该组件将它们转换为可以理解的东西即可。 这种用法并不常见,如果你想扩展JSX的其他能力,可以通过这个例子了解下它的工作原理。

布尔值、null、undefined在渲染时会被自动忽略

falsenullundefinedtrue是有效的子元素,不过他们从根本上讲是不参与渲染的。 这些JSX表达式将渲染处相同的东西:

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{true}</div>

这对于有条件地呈现React元素很有用。 如果showHeadertrue,那么这个JSX只渲染一个<Header />

<div>
    {showHeader && <Header />}
    <Content />
</div>

如果返回一些“假的”值就会收到一个警告,如数字0,不过React仍然会渲染。 例如,此代码将不会像您预期的那样工作,因为当props.messages是空数组时将打印0

<div>
    {props.messages.length && <Message messages={props.messages} />}
</div>

想要修复上面的问题,你要确定这个表达式在&&之前总返回布尔值:

<div>
    {props.messages.length > 0 && <Message messages={props.messages} />}
</div>

相反,如果你想要一个值如falsetruenullundefined出现在输出中,你必须先将它转换为字符串

import React from 'react';
import ReactDOM from 'react-dom';

function MyVariable(props) {
    const myVariable = false;
    // 如果这里不把false转换为字符串,这只会输出『我的javascript变量是』
    const convertedVar = String(myVariable);
    return (
        <div>
            我的javascript变量是{convertedVar}
        </div>
    );
}
ReactDOM.render(
    <MyVariable/>,document.getElementById('root')
);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom