65.9K
CodeProject 正在变化。 阅读更多。
Home

通过示例学习 ReactJS 入门

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2018 年 1 月 26 日

CPOL

10分钟阅读

viewsIcon

14961

downloadIcon

383

通过一个简单的下拉 UI 元素示例学习 ReactJS。

引言

我最近读到一项统计数据,表明 ReactJS 已迅速成为最流行的前端 Web 技术之一。因此,作为一名职业软件开发人员,我认为是时候学习它了。

学习 ReactJS 时,你会首先注意到它基于组件;也就是说,一个 ReactJS 应用,包括应用本身,都是由组件组成的。因此,我认为使用新技术创建的最合乎逻辑的组件类型将是某种 UI 组件或小部件。过去,我曾纯粹使用 JavaScript 或 TypeScript 创建过模仿现有 HTML 元素但更易于使用的 UI 小部件(尤其是在 JavaScript 中)。所以,我决定基于 HTML select 元素创建一个“ListBox”(或“DropDown”框)。

关于 ReactJS,你应该了解的下一件事是,虽然不是绝对必要,但它通常会利用一种名为 JSX 的技术。JSX 是 JavaScript 的一种类似 XML 的语法扩展,它允许你在 JavaScript 中嵌入 UI 标记。这样做的一个主要好处是,它允许 UI 标记和相关逻辑嵌入到同一个组件中,正如上面所说,这与 ReastJS 的基于组件的模型非常契合。虽然我不会详细介绍 JSX,因为它是一项独立的技术,但我在这里提到它是因为它在下面的示例中使用。

ReactJS 开发中还有一个方面应该注意,那就是它利用了最新的 JavaScript 规范 ECMAScript 6。由于这一点以及与 JSX 的集成,它通常需要像 Babel 这样的转译器将应用程序转换为纯 ECMAScript 5 JavaScript 代码。我也不会详细介绍如何配置你的 ReactJS 开发环境。

现在让我们深入代码,详细了解 ReactJS 的工作原理以及它在简化前端开发方面提供的优势。

Components

组件通常是通过使用 ECMAScript 6 的类语法并从 React.Component 类扩展(在面向对象的说法中也称为“继承”)来创建的,如下所示:

        class App extends React.Component
        {
        }

当然,我们可以给这个类起任何名字,但通过将这个特定的类命名为 'App',它就相当明显了,这是我们的“应用程序”组件,代表了我们应用程序的最高级别组件。要“启动”应用程序,你只需调用 ReactDOM 类上的 render 方法,并将你的应用程序组件和它将要渲染到的 HTML 元素传递进去,如下所示:

        ReactDOM.render(<App />, document.getElementById('content'));

应用程序组件中的所有组件都将通过调用每个组件的“render”方法自动渲染;因此,你需要为你创建的每个组件创建一个 'render' 方法。这个 'render' 方法是你定义组件 UI 布局的地方,通常由标准的 HTML 元素与 JSX 元素组合而成。下面是我们应用程序类的 render 方法,其中两个 ListBox 元素是基于我们新的 ListBox 组件的 JSX 元素。

        render()
        {
            return (
                <div>
                    <h1>Car Selection</h1>
                    <ListBox Items={this.state.carMakes}
                             valuePropertyName='id'
                             selectedValue={this.state.selectedMake}
                             placeholder='Please select a make...'
                             textPropertyName='name'
                             OnSelect={this.OnCarMakeSelect} />

                    <ListBox Items={this.state.carModels}
                             selectedValue={this.state.selectedModel}
                             placeholder='Please select a model...'
                             OnSelect={this.OnCarModelSelect} />

                 </div>
            );
        }

在此示例中,我们的应用程序的“top-level”元素(忽略我们在页面上放置应用程序组件的元素)是一个“div”元素,它包含一个“h1”元素以及两个 ListBox 组件。

属性

如你所见,我们可以为 JSX 元素定义任意数量和类型的属性,并为其分配值。这些元素成为相应子组件的“属性”。请注意,为了在 JSX 语法中包含 JavaScript 代码,必须将其用花括号括起来。

现在我们来看看 ListBox 组件定义及其 render 方法,以了解如何访问组件声明中定义的属性。

        class ListBox extends React.Component
        {
            render() 
            {
                return (
                    <div className="listBox">
                        <select onChange={this.OnChange.bind(this)} ref='listBox'>
                            <option value="-1" disabled selected hidden>{this.props.placeholder}</option>
                            {this.props.Items.map((item, i) => 
                            <ListBoxItem Item={{ 'Text': item[this.textPropertyName], 
                            'Value': item[this.valuePropertyName] }} />)}
                        </select>
                    </div>
                ); 
            }
        }

你将立即注意到,分配给组件的属性是通过一个名为 'props' 的对象访问的,该对象会自动为你创建。正如我在本文开头提到的,我们的 listbox 是基于 'select' HTML 元素的,这里就是定义它的地方。虽然在我们的 list box 组件中它并没有提供太多好处,但我还为每个 'option' 元素创建了一个 ReactJS 组件,由下面的 ListBoxItem 组件表示。

        class ListBoxItem extends React.Component
        {
            render() 
            {
                return (
                        <option key={this.props.Item.Value} 
                        value={this.props.Item.Value}>{this.props.Item.Text}</option>
                );
            }
        }

我们通过属性向下传递两个值(通过属性)给每个 ListBoxItem 组件,分别代表每个 option HTML 元素的 value 和 text。建议在组件代表列表中的一项时添加一个 'key' 属性,因为 ReactJS 使用此属性来识别哪些项已更改、已添加或已删除。

到现在应该很明显了,我们的 list box 组件被简单地渲染为一个 'select' HTML 元素。

虽然这与 ReactJS 本身无关,但值得注意的是,我们在 listbox 中定义的第一个 option 元素是为了给 select HTML 元素提供占位符文本,因为 select 元素没有标准的 'placeholder' 属性。我们还用它来实现在首次渲染 listbox 时不选择任何选项。

这里还值得注意的是,这个 ListBox 组件提供了一种方法来“声明”Items 列表中表示每个选项元素“value”和“text”属性的对象上的属性名称。正如你在下面的代码中看到的,使用 JavaScript 访问数组中对象字段的其中一种方法(通过使用方括号)可以轻松实现这一点。

        <ListBoxItem Item={{ 'Text': item[this.textPropertyName], 
        'Value': item[this.valuePropertyName] }} />

现在,让我们回到 ReactJS。

状态

ReactJS 的主要好处之一是状态的处理方式。在任何应用程序中维护和操作状态都可能很麻烦,并且难以调试。建议将状态维护在尽可能高的级别,这通常是在应用程序组件中。在开发像我们的 list box 这样的组件时,很容易在 ListBox 组件本身中放置某种类型的状态;例如,用于跟踪 listbox 中当前选定的项目(或值)。然而,在这种情况下,ReactJS 的做法是将当前选定的项目存储在应用程序状态中。

每个组件的状态应存储在名为 'state' 的对象中,并且 state 使用名为 'setState' 的方法进行操作(该方法更新 state 对象)。这是我们在应用程序组件中最初定义的 state

        this.state =
        {
            carMakes: GetCarMakes(),
            selectedMake: -1,
            carModels: [],
            selectedModel: -1
        }

GetCarMakes() 只是我们组件之外的一个 JavaScript 函数,它返回一个汽车品牌数组。carModels 是一个初始定义为空数组的状态属性,每次选择新品牌时都会对其进行操作。

selectedMakeselectedModel 是用于存储来自相应 ListBox 组件的当前选定的品牌和型号的两个属性。将这些跟踪在应用程序组件中是有意义的,因为,在某个时候,我们很可能会将这些选择以及其他数据保存到某种后端存储中。

在 ReactJS 中,建议 state 是不可变的,例如,而不是直接修改数组,数组会在每次更改时重新创建。这是通过调用 'setState' 方法完成的。此方法的好处是,你只需“set”正在更改的属性——任何其他现有状态属性仍将作为 state 的一部分。例如,看看我们应用程序组件的 OnCarMakeSelect 方法的代码:

        OnCarMakeSelect(value, text)
        {
            this.setState({ selectedMake: value, carModels: GetCarModels(value), selectedModel: -1 });
        }

我们稍后会讨论事件和事件处理程序,但只需知道每当选择一个新的汽车品牌时都会调用此方法。在这种情况下,我们会更新当前选定的品牌和汽车型号数组(同时将当前选定的型号设置为 -1,以便在型号 ListBox 中不选择任何默认选项并显示占位符)。

数据绑定。

现在是时候谈谈 ReactJS 中的绑定了。任何与 UI 交互的 JavaScript 库或框架都不可能不提供某种形式的绑定。不幸的是,虽然许多这类库(例如 KnockoutJS)提供双向绑定,但 ReactJS 只提供单向绑定。这种绑定的方向是从状态到 UI,没有从 UI 到状态的相应绑定。换句话说,你需要在 UI 中发生变化时处理状态更新,这通常通过 DOM 事件结合事件处理程序来完成。

State 是在组件声明时与其声明性地绑定的。例如,正如我们上面所见的:

        <ListBox Items={this.state.carMakes}
                 valuePropertyName='id'
                 selectedValue={this.state.selectedMake}
                 placeholder='Please select a make...'
                 textPropertyName='name'
                 OnSelect={this.OnCarMakeSelect} />

        <ListBox Items={this.state.carModels}
                 selectedValue={this.state.selectedModel}
                 placeholder='Please select a model...'
                 OnSelect={this.OnCarModelSelect} />

this.state.carMakesthis.state.selectedMake 绑定到第一个 ListBox,而 this.state.carModelsthis.state.selectedModel 绑定到第二个。

作为绑定过程的一部分,ReactJS 的一个更强大的功能是它能够根据状态的变化来确定哪些组件需要被重新渲染(据我所知,是 JSX 让这一切变得更容易)。这使得 ReactJS 比其他 JavaScript 库更有效。此过程在调用 'setState' 方法时执行。例如,在上面显示的 OnCarMakeSelect 方法中,由于 carModelsselectedModel 状态属性“绑定”到了汽车型号 ListBox 组件,因此该组件将自动更新(即重新渲染)。

事件和事件处理程序

正如我上面提到的,DOM 事件和事件处理程序是处理从 UI 到状态的绑定的必需品。首选的方法是将事件处理函数作为属性发送给子组件,并在存储状态的组件内部更新状态(以及执行任何其他操作)。在我们的示例中,我们在应用程序组件中创建了两个事件处理函数,如下所示:

        OnCarMakeSelect(value, text)
        {
            this.setState({ selectedMake: value, carModels: GetCarModels(value), selectedModel: -1 });
        }

        OnCarModelSelect(value, text)
        {
            this.setState({ selectedModel: value });
        }

注意,我们为这些函数中的每一个定义了两个参数。我们通过一个名为 OnSelect 的属性将这些函数传递给 ListBox 子组件,如下所示:

        <ListBox Items={this.state.carMakes}
                 valuePropertyName='id'
                 selectedValue={this.state.selectedMake}
                 placeholder='Please select a make...'
                 textPropertyName='name'
                 OnSelect={this.OnCarMakeSelect} />

        <ListBox Items={this.state.carModels}
                 selectedValue={this.state.selectedModel}
                 placeholder='Please select a model...'
                 OnSelect={this.OnCarModelSelect} />

ListBox 组件中,我们创建了一个处理程序函数,该函数从 select HTML 元素的 onChange 事件调用。该函数如下:

        OnChange(event)
        {
            this.props.OnSelect(event.target.value, 
                                event.target.options[event.target.selectedIndex].text);
        }

因此,我们从默认传递到此处理程序函数的 DOM 事件中获取数据,并将 select 元素中选定项的选定值和关联文本传递给分配给 OnSelect 属性的父事件处理程序。(这样,父组件就不需要理解或担心 DOM 事件是什么了。)

除了其中一件事情之外,就这样了。你可能已经注意到了应用程序组件的构造函数方法中的以下代码:

        this.OnCarMakeSelect = this.OnCarMakeSelect.bind(this);
        this.OnCarModelSelect = this.OnCarModelSelect.bind(this);

由于 JavaScript 中类方法默认没有绑定,因此你必须将 this 对象实例绑定到传递给组件的每个方法;否则,当最终调用 this 属性时,它将是未定义的。

ReactJS 中属于此示例且我尚未讨论的最后一个方面是组件生命周期的概念。每个组件都有一个生命周期,在初始化和渲染的不同阶段可以处理某些事件,你可以实现方法在这些不同阶段注入你自己的逻辑。我将把研究不同事件/方法的任务留给读者,但我将解释我们在示例中实现的一个。下面是此事件处理程序的代码:

        componentDidUpdate(nextProps, nextState)
        {
            var select = this.refs.listBox;
            if (select)
            {
                select.value = this.props.selectedValue;
                this.props.OnSelect(select.value);
            }
        }

componentDidUpdate 在每次渲染后被调用。我们在示例中实现它,因为汽车型号列表可以根据所选的品牌而改变,并且我们希望重置选定的值(如果我们在列表更改时希望不选择任何值,则为 -1)。请注意 'refs' 属性的使用。这是一个巧妙的 ReactJS 属性,它允许你引用组件中定义的任何 DOM 元素的 DOM 元素。这与可以在每个 DOM 元素上定义的 'ref' 属性结合使用。

结论

我希望通过这个相当小的示例,我已经触及了 ReactJS 的重要概念,并且这将为你的 ReactJS 开发提供一个重要的良好开端。

历史

  • 2018 年 1 月 25 日 - 首次上传
© . All rights reserved.