React中创建组件的方式

学习React也有一段时间了,虽然天天都在围绕着组件打转转,但在React中怎么构建组件呢并没有去深入的了解。事实上呢?在React中的组件还是有些复杂的,从概念上来说就不简单。比如说, 类组件
函数组件
无状态组件
高阶组件
等。另外创建组件的方式也有所不同,比如最早使用 React.createClass
来创建组件,有了ES6之后使用 extends React.Component
(借助ES6的 class
特性)创建组件,而今天又流行使用函数(Hooks)方式来创建组件。那么他们之间如何创建组件,又有何区别呢?在这篇文章中我们就先来学习和探讨在React中如何创建组件。

先来看 React.createClass
如何创建组件?

React.createClass

如果你一直以来都在使用React的话,那么对 React.createClass
这个API并不陌生。在React中,最初就是用这个API来创建React组件。将描述组件的所有信息都将作为对象传递给 createClass

createClass
方法为开发人员提供了一个工厂方法(Factory Method),可以在不使用JavaScript 类的情况下创建React类组件。这是在ES之前创建React组件方法之一,因为在ES5中没有可用的类语法:

const App = React.createClass({
    getInitialState: function() {
        return {
            value: '大漠'
        }
    }

    onChange: function(e) {
        this.setState({
            value: e.target.value
        })
    }

    render: function() {
        return (
            

使用 React.createClass 创建组件

Hello, {this.state.value} (^_^)!
) } }) const rootElement = document.getElementById("app"); ReactDOM.render(, rootElement);

上面的Demo在React V15.5版本上运行。

createClass()
方法接受一个对象,该对象为React组件定义方法。 getInitialState()
函数用于为React组件设置 初始状态
,强制使用 render()
方法在JSX中用于输出;额外的方法(比如 onChange
)是通过向对象传递更多的函数而添加的。

React中的生命周期也是可用的。例如,为了每次将值从 input
中输入存到浏览器的本地存储中( localStorage
),我们可以使用 componentDidUpdate()
生命周期,该方法是将一个函数传递给对象,对象键以React的生命周期方法命名。此外,当组件接收到初始状态时,可以从本地存储中读取该值:

const App = React.createClass({
    getInitialState: function() {
        return {
            value: localStorage.getItem('userName') || '@大漠'
        }
    },
    componentDidUpdate: function(){
        localStorage.setItem('userName', this.state.value)
    },
    onChange: function(e) {
        this.setState({
            value: e.target.value
        })
    },
    render: function() {
        return (
            

使用React.createClass创建组件

Hello, {this.state.value} (^_^)!
) } }) const rootElement = document.getElementById("app"); ReactDOM.render(, rootElement);

这个示例具有本地存储的功能,每当重新加载或刷新浏览器时,当组件第一次挂载时,应该会显示之前在 input
中输入的本地存储的初始状态。

注意:React核心包中不再提供 React.createClass()
方法。如果你想尝试它,必须安装一个额外的包: npm i create-react-class
。时至今日,应该尽可能地避免使用它。
在这里可以获取到 React.createClass()
创建组件更多的信息

React Mixins

React中引入了React Mixins,作为React的第一个可重用组件逻辑,这是一种高级模式。使用Mixin,可以将React组件的逻辑提取出来成为一个 独立对象
。当在组件中使用Mixin时,所有来自Mixin的特性都被引入组件:

var localStorageMixin = {
    getInitialState: function(){
        return {
            value: localStorage.getItem('userName') || '@大漠'
        }
    },
    setLocalStorage: function(val) {
        localStorage.setItem('userName', val)
    }
}

var App = React.createClass({
    mixins: [localStorageMixin],
    componentDidUpdate: function(){
        this.setLocalStorage(this.state.value)
    },
    onChange: function(e) {
        this.setState({
            value: e.target.value
        })
    },
    render() {
        return (
            

使用React Mixin和createClass创建组件

Hello, {this.state.value} (^_^)!!!
) } }) const rootElement = document.getElementById("app"); ReactDOM.render(, rootElement);

本例中,Mixin提供从本地存储中读取组件的初始状态,并使用 setLocalStorage()
方法扩展组件,该方法稍后将在实际组件中使用。为了使用Mixin更加灵活,我们可以使用一个函数来返回一个对象:

function getLocalStorageMixin(localStorageKey) {
    return {
        getInitialState: function(){
            return {
                value: localStoragee.getItem(localStorageKey) || ''
            }
        },
        setLocalStorage: function(value) {
            localStorage.setItem(localStorageKey, value)
        }
    }
}

var App = React.createClass({
    mixins: [getLocalStorageMixin('userName')],
    // ...
})

注意:现在在React中不再使用Mixins了,因为它们有几个缺点。 有关于React Mixins更多的信息,可以点击这里进行了解

createClass()
是创建React组件的一种简单而有效的方法。React最初使用 createClass
API的原因是,当时JavaScript没有内置的 class
。当然,这种情况最终改变了。ES6开始引入了 class
这个关键字,也可以使用类来创建组件。这让React进入了一个两难的境地,
要么继续使用 createClass
,要么跟进ES6,使用 class
来创建组件

。事实证明,React选择了后者。

React.Component

React v3.13.0
版本引入了 React.Component
API,允许你使用JavaScript的类( class
)来创建React组件。在React中使用 class
创建的组件常常被称为 React 类组件

我们可以使用 React.Component
来重构上面使用 React.createClass()
方法创建的组件。

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: localStorage.getItem('userName') || '@大漠'
        }
        this.onChange = this.onChange.bind(this)
    }

    componentDidUpdate() {
        localStorage.setItem('userName', this.state.value)
    }

    onChange(e) {
        this.setState({
            value: e.target.value
        })
    }

    render() {
        return (
            

使用ES6 Class创建组件(React.Component)

Hello, {this.state.value} (^_^)!!!
) } } const rootElement = document.getElementById("app"); ReactDOM.render(, rootElement);

使用JavaScript类编写React组件带有类构造函数 constructor()
(主要用于React中设置初始状态或绑定方法)和 render()
方法。React组件内部所有逻辑都来自于 React.Component
。通过类组件中使用面向对象继承的组件。但是,不建议在更多的地方使用继承这个概念。相反,建议 使用组合而不是继承

在React中使用 React.Component
创建组件,有几个重要的概念需要掌握。

构造函数 constructor()

使用类组件,可以在构造函数 constructor()
内部将组件的状态初始化为实例( this
)上的状态属性。但是,根据ECMAScript规范,如果要扩展子类(即 React.Component
),必须要先调用 super()
,然后才能使用 this
。具体来说,在使用React时,还必须记住将 props
传递给 super()

class App extends React.Component {
    constructor(props) {
        super(props)
        // ...
    }
    // ...
}

自动绑定

当使用 React.createClass
创建组件时,
React会自动将所有方法绑定到组件的实例( this

。而 React.Component
并非如此,很多开发人员都意识到他们不知道 this
关键字是如何工作的。因为必须记住
类构造函数中的 .bind()
方法(即 .bind(this)

。如果不这样做的话,浏览器会报“
无法读取未定义的 setState
属性

”错误。

class App extends React.Component {
    constructor(props) {
        super(props)
        //...
        this.onChange = this.onChange.bind(this)
    }
    // ...
}

调用 super(props)
并要记住 .bind(this)
方法是比较烦人,但这里并没有什么根本的错误。但当你一天要像这样处理很多次的时候,也会令人感到烦感。庆幸的是,在从 createClass
切换到 React.Component
之后不久,
TC39就提出 Class Fields
相关的建议

类字段(Class Fields)

类字段允许我们直接将实例属性作为属性添加到类上,而无需使用构造函数。这样一来,我们就不再需要使用构造函数来设置组件的初始状态,也不再需要在构造函数中使用 .bind(this)
,因为我们可以使用箭头函数。

class App extends React.Component {
    state = {
        value: localStorage.getItem('userName') || '@w3cplus'
    }

    componentDidUpdate() {
        localStorage.setItem('userName', this.state.value)
    }

    onChange = (e) => {
        this.setState({
            value: e.target.value
        })
    }

    render () {
        const {value} = this.state

        return (
            

使用React.Component创建组件(Class Fields)

Hello, {value} (^_^)!!!
) } } const rootElement = document.getElementById("app"); ReactDOM.render(, rootElement);