React 组件模式学习总结

774 阅读4分钟

容器组件(Container)

容器组件是你的数据或逻辑层并利用 stateful API,使用生命周期事件,你可以连接 state 到 redux 或者 Flux 的 storage 中,并将数据和回调作为 props 传递给子组件。

负责与Store交互 

容器组件自身不会触发action(他只是披在傻瓜组件上的一层壳) 

内:向傻瓜组件传递由Store获得的props 

外:向Store派发用户操作傻瓜组件引起的action 

父组件

import React, {PureComponent} from "react"
import PropTypes from 'prop-types';
import axios from "axios"
import ProductList from "views/product-list"
class NwdContainer extends React.Component {
    constructor(props){
        super(props)
    }
    state = {
        names: ""
    }
    componentDidMount() {
        var _this = this;
        axios.get('https://api.github.com/search/users?q=tom+repos:%3E42+followers:%3E1000')
            .then(function (res) {
                _this.setState({
                    names: res.data.total_count
                })
            })
    }
    render() {
        return (
            <div>
                <ProductList names={this.state.names}></ProductList>
            </div>
        )
    }
}
export default NwdContainer

子组件(展示组件)

import React, {PureComponent} from "react"
import PropTypes from 'prop-types';
const ProductList = (props) => {
    return (
        <div>
            {props.names}
        </div>
    )
}
export default ProductLis

如上 容器组件,就是负责获取用户数据,然后以props的形式传递给展示组件ProductList来渲染。容器组件也不会在页面中渲染出具体的DOM节点,因此,它通常就充当数据源的角色。目前很多常用的框架,也都采用这种组件形式。如:React Redux的connect(), Relay的createContainer(), Flux Utils的Container.create()等。

展示组件

展示组件 使用 props、render 和 context (无状态API),并且由于不需要使用生命周期相关 Api,可以使用纯函数来简化编写函数式组件 
import React, {PureComponent} from "react"
import {PropTypes} from 'prop-types';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'

const NwdCard = (props) =>{
    console.log(props)
    return (
        <div>{props.name}</div>
    )
}

export default  NwdCard
展示组件 仅从 props 接收数据和回调,这些数据和回调可以由其容器组件(父组件)提供

父组件

import React, {PureComponent} from "react"
import {PropTypes} from 'prop-types';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'
import NwdCard from "views/nwd-card";
export default class Form extends React.Component {
    constructor(props) {
        super(props)
    }
    state = {
        name: "你我贷1"
    }
    componentDidMount() {
        this.setState(() =>{
            return {
                name:"你我贷2"
            }
        })
    }
    render() {
        return (
            <div>
               <NwdCard name={this.state.name}></NwdCard>
            </div>
        )
    }
}

子组件

import React, {PureComponent} from "react"
import {PropTypes} from 'prop-types';
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'



const NwdCard = (props) =>{
    console.log(props)
    return (
        <div>{props.name}</div>
    )
}

export default  NwdCard

输出:


高阶组件

高阶组件是一种函数,它接受一个组件作为参数,然后返回一个新的组件

比如说 react-router-dom提供的withRouter就是典型的高阶组件,可以先说说withRouter的作用

一般路由组件可以直接访问当前路由的match,location,history,当一个非路由组件也想访问到当前路由的match,location,history对象,那么withRouter将是一个非常好的选择,可以理解为将一个组件包裹成路由组件。

路由组件:

import React, {PureComponent} from "react"
import PropTypes from 'prop-types';

import TestRouterChild from "views/test-routerchild"

class TestWithRouter extends React.Component {
    constructor(props){
        super(props)
    }
    componentDidMount() {
        console.log("路由组件输出--------------")
        console.log(this.props)
        console.log(this.props.location.pathname)
    }
    render() {
        return (
            <div>
                你我贷
                <TestRouterChild></TestRouterChild>
            </div>
        )
    }
}
export default TestWithRouter

非路由组件 (子组件):

import React, {PureComponent} from "react"
import PropTypes from 'prop-types';

class TestRouterChild extends React.Component {
    constructor(props){
        super(props)
    }
    handle = () => {
        this.props.history.push("life")
    }
    render() {
        console.log("子路由输出---------------------")
        console.log(this.props)
        console.log(this.props.location)
        return (
            <div>
                <div>子组件</div>
                <button onClick={this.handle}>click</button>
            </div>
        )
    }
}
export default TestRouterChild

输出如下 



通过上图看出:绿色的是非路由组件中的输出,红色的是路由组件的输出,因此子路由即非路由组件是无法获取当前路由的参数,点击跳转也无法跳转到对应的页面

引入withRouter

非路由组件(子组件)

import React, {PureComponent} from "react"
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom'

@withRouter
class TestRouterChild extends React.Component {
    constructor(props){
        super(props)
    }
    handle = () => {
        this.props.history.push("life")
    }
    render() {
        console.log("子路由输出---------------------")
        console.log(this.props)
        console.log(this.props.location)
        return (
            <div>
                <div>子组件</div>
                <button onClick={this.handle}>click</button>
            </div>
        )
    }
}
export default TestRouterChild

输出如下:



通过上图可以观察到 引入withRouter后 非路由组件(子路由)可以获取当前的location  history 

如果不用@withRouter  可以使用  export default withRouter(TestRouterChild)


渲染回调组件(Render Callback)

渲染回调或渲染属性(props) 被用于共享或重用组件逻辑,将组件中的渲染逻辑委托给其子组件

父组件

import React, {PureComponent} from "react"
import PropTypes from 'prop-types';
import CallbackChild from "views/callback-child"
class NwdCallback extends React.Component {
    constructor(props){
        super(props)
    }
    render() {
        return (
            <div>
                <CallbackChild>
                    {
                        (msg) => (
                            <div>
                                我的名字叫: {msg}
                            </div>
                        )
                    }
                </CallbackChild>
            </div>
        )
    }
}
export default NwdCallback

子组件

import React, {PureComponent} from "react"
import PropTypes from 'prop-types';
class CallBackChild extends React.Component {
    constructor(props){
        super(props)
    }
    state = {
        name: "飞旋的留恋"
    }
    componentDidMount() {
    }
    render() {
        console.log(this.props)
        console.log(this.props.children)
        return (
            <div>
                {this.props.children(this.state.name)}
            </div>
        )
    }
}
export default CallBackChild

如下



注释:虽然前端开发更喜欢使用 高阶组件的可重用逻辑,但是使用 渲染回调 仍然有一些非常好的理由和优势:渲染回调减少了命名空间冲突,并更好的说明了逻辑来源。