将反应组件传递到更高阶函数的替代方法

时间:2018-06-18 16:02:36

标签: reactjs higher-order-functions higher-order-components

我使用ReactJS更高阶函数来增强现有组件的API获取功能以及加载,错误视图。为了更加可重用,我希望使用我的HOC的另一个程序员能够添加自定义加载,这样的错误视图。

var FetchTest = fetchableContainer({
    url:"https://jsonplaceholder.typicode.com/posts",
    loadingView: <div>..Custom Loading..</div>,
    noConnectionView: <div>.. Custom no connection view .. </div>,
    errorView: <div>Custom Error View</div>
})(TestComponent);

不幸的是,它显示错误消息。

  

元素类型无效:需要一个字符串(用于内置组件)   或类/函数(对于复合组件)但得到:object。

有人可以用干净优雅的代码告诉我另一个解决方案。这是我的fetchableContainer。

import React, {Component} from 'react';
import axios from 'axios';
import loadingView from './loadingView.js';
import errorView from './errorView.js';
import noDataView from './noDataView.js';
import noConnectionView from './noConnectionView.js';

//redux imports
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import { Link } from 'react-router';


const fetchableContainer = (json) => (BaseComponent) => {

    let url = json.url || "";
    let loadingView = json.loadingView || loadingView;
    let errorView = json.errorView || errorView;
    let noConnectionView = json.noConnectionView || noConnectionView;
    let noDataView = json.noDataView || noDataView;

    class FetchableContainer extends React.Component{

        constructor(props){
        super(props);  
        this.state = {
        fetchData: null,
        loading: false,
        fetchError: null,
        interntConnection: navigator.onLine?true:false,
        };

    }

    componentDidMount(){
        this.setState({loading: true});
        axios.get(this.url,{
            headers: { 
                'Access-Control-Allow-Origin' : '*',
                'Access-Control-Allow-Methods' : 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
            }

        }).then((response)=>{
            this.setState({fetchData: response.data});
            this.setState({loading: false});
        }).catch((error)=>{
            console.log("Erorr happen");
            console.log(error);
            this.setState({fetchError: error});
        });
    }

    render(){

        if(!this.state.interntConnection){
            return <this.noConnectionView/>; 
        }

        if(this.state.loading){
            return <this.loadingView/>;
        }

        if(this.state.fetchError){
            return <this.errorView/>;
        }

        return (
        <BaseComponent {...this.props}{...this.state}/>
        );
    }
}
}

export default fetchableContainer;

2 个答案:

答案 0 :(得分:1)

首先,您的FetchTestundefined,因为fetchableContainer并未返回任何内容!从技术上讲,它返回一个不返回任何内容的函数。如果你真的想要使用它,你应该返回该类。

此外,这似乎是创建组件的一种奇怪方式。目前,这相当于这样做:

var FetchTest = fetchableContainer({
    url:"https://jsonplaceholder.typicode.com/posts",
    loadingView: <div>..Custom Loading..</div>,
    noConnectionView: <div>.. Custom no connection view .. </div>,
    errorView: <div>Custom Error View</div>
}, TestComponent);

容器:

//...

const fetchableContainer = (json, BaseComponent) =>

    class FetchableContainer extends React.Component {

      //...
    }
}

export default fetchableContainer;

可能会显示错误消息,因为您尝试在某些尚未发布的代码中使用undefined FetchTest

我建议以标准方式(https://redux.js.org/basics/usage-with-react#implementing-container-components)创建一个React容器,并将所需的参数传递给props

例如,它可能看起来像这样:

// Imports etc...

class FetchableContainer extends React.Component {
    // ...
    render() {
        //...
        return this.props.BaseComponent(/* Props */);
    }
}

export default FetchableContainer; 

答案 1 :(得分:0)

我刚发现错误。这是因为我传递了JSX组件并尝试获取该JSX组件。错误是通过传递返回JSX组件的函数并在FetchableContainer的render方法上获取该函数来解决的。

var FetchTest = fetchableContainer({
    url: 'https://jsonplaceholder.typicode.com/posts/',
    errorView: ()=> <div>Custom Error View</div>,
    LoadingView:()=><div>Custom loading</div> ,
    NoConnectionView: ()=> <div>Custom no Connection</div>
})(TestComponent);

我对FetchableContainer的最终代码如下。

import React, {Component} from 'react';
import axios from 'axios';
import LoadingView from './LoadingView';
import ErrorView from './ErrorView';
import NoDataView from './NoDataView';
import NoConnectionView from './NoConnectionView';

//redux imports
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import { Link } from 'react-router';


const fetchableContainer = (json) => (BaseComponent) =>{

    let LoadingFetchView = json.LoadingView || LoadingView;
    let fetchUrl = json.url || "https://jsonplaceholder.typicode.com/posts/";
    let ErrorFetchView = json.ErrorView || ErrorView;
    let NoConnectionFetchView = json.NoConnectionView || NoConnectionView;
    let NoDataFetchView = json.NoDataView || NoDataView;

class FetchableContainer extends React.Component{

        constructor(props){

            console.log("inside constructure");
            console.log(LoadingView);

        super(props);  
        this.state = {
        fetchData: null,
        loading: false,
        fetchError: null,
        interntConnection: navigator.onLine?true:false,
        };



    }

    componentDidMount(){
        this.setState({loading: true});
        axios.get(fetchUrl,{
            headers: { 
                'Access-Control-Allow-Origin' : '*',
                'Access-Control-Allow-Methods' : 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
            }

        }).then((response)=>{
            this.setState({fetchData: response.data});
            this.setState({loading: false});
        }).catch((error)=>{
            console.log("Erorr happen");
            console.log(error);
            this.setState({fetchError: error});
        });
    }

    render(){

        if(!this.state.interntConnection){
            return <NoConnectionFetchView/>; 
        }

        if(this.state.loading){
            return <LoadingFetchView/>;
        }

        if(this.state.fetchError){
            return <ErrorFetchView/>;
        }

        return (
        <BaseComponent {...this.props}{...this.state}/>
        );
    }
}
return FetchableContainer;
}

export default fetchableContainer;