ReactJS 中的高阶组件 - 从数据组件扩展功能 [英] Higher-Order Components in ReactJS - extend functionalities from data components

查看:48
本文介绍了ReactJS 中的高阶组件 - 从数据组件扩展功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于这个愚蠢的问题,我很抱歉,我正在学习 React,所以我根本不是专家.

I'm sorry for the stupid question, I'm learning React so I'm not expert at all.

我想知道是否有一种方法可以使用某些功能扩展我的无状态组件,而无需始终围绕它们创建包装器,以便它们连接到 Redux 存储并通过 props 传递数据.

I'm wondering if there is a way to extend my stateless components with some functionalities without creating always wrappers around them that they'll connect to the Redux store and pass data through props.

高阶组件是实现此目的的最佳方法吗?

Is it the higher order component the best way to do that?

让我用几行代码更好地解释.下面的示例组件应该在用户点击事件上调用 API/favourites (POST) 以创建Favourite"实体,并且它将调用相同的 API (DELETE) 以删除它.

Let me explain better with a few lines of code. The example component below should call an API /favourites (POST) on the user click event in order to create a "Favourite" entity and it will call the same API (DELETE) in order to delete it.

这是我已经意识到的解决方案:

This is the solution that I've realized:

const HOC_DISPLAY_NAME = 'HOCFavouriteIcon';
export default function HOCWrapperFavourite(FavouriteComponent) {

return class extends React.Component {

    static displayName = HOC_DISPLAY_NAME;

    constructor(props) {
        super(props);
        this.state = this.getDefaultState();
        this.bindMethods();
    }

    getDefaultState() {
        return {
            isFavourite: false,
            addFavouritePayload: null,
            removeFavouritePayload: null,
        };
    }

    render() {
        return (
            <DataProviderApiFavourites
                create={this.state.addFavouritePayload}
                createListener={this.favoriteCreateHandler}
                delete={this.state.removeFavouritePayload}
                deleteListener={this.favoriteDeleteHandler}
            >
                <FavouriteComponent
                    {...this.props}
                    {...this.state}
                    addFavourite={this.addFavouriteClickHandler}
                    removeFavourite={this.removeFavouriteClickHandler}
                />
            </DataProviderApiFavourites>

        );
    }

    addFavouriteClickHandler(visitorId, artId) {
            this.setState({
                addFavouritePayload: {visitorId, artId},
            });
    }

    removeFavouriteClickHandler(visitorId, favouriteId) {
            this.setState({
                removeFavouritePayload: {visitorId, favouriteId},
            });
    }

    favoriteCreateHandler(result){
        // do something when a favorite entity was created
    }

    favoriteDeleteHandler(result){
        // do something when a favorite entity was deleted
    }

    bindMethods() {
        this.addFavouriteClickHandler = this.addFavouriteClickHandler.bind(this);
        this.removeFavouriteClickHandler = this.removeFavouriteClickHandler.bind(this);
        this.favoriteCreateHandler = this.favoriteCreateHandler.bind(this);
        this.favoriteDeleteHandler = this.favoriteDeleteHandler.bind(this);
    }

}

};

DataProviderApiFavourites 有一个属性create",它接受一个 JSON 负载,它会触发一个由 Saga 捕获的 Redux 操作(它将调用一个 API),它也能够通过 createListener 函数通知结果.它对删除属性执行相同的操作(但它会调用删除 API 并通过 favoriteDeleteHandler 进行通知).

The DataProviderApiFavourites has a property "create" that accepts a JSON payload, it will trigger a Redux action caught by Saga ( who will call an API ) and it also is able to notify the result through createListener function. It does the same with the delete prop ( but it will call the delete API and will notify through favoriteDeleteHandler ).

考虑到 FavouriteComponent 是一个无状态组件,它是否是一个很好的结构,以便在 UI 组件中使用数据组件的功能?

Considering that the FavouriteComponent is a stateless component, is it a good structure in order to use functionality from data components in UI components?

实际上,DataProviderApiFavourites 可以在不嵌套子组件的情况下使用,如果设置,则返回 null 或 this.props.children.

Actually, the DataProviderApiFavourites could be used without nesting the child component, it returns null or this.props.children if is set.

如果有人能帮助我理解我是否会做错事,我将不胜感激.

I'll really appreciate if someone will help me to understand if I'm going to do something wrong.

推荐答案

当然,HOC 非常适合这个用例.

Sure, HOC would fit this use case perfectly.

代码很好,但是,为了坚持组合和可重用性的想法,我会构建一个更通用的 HOC 并且不要在其中放置太多逻辑,然后如果您需要更具体的东西,请传递参数或在其上构建另一个 HOC.

The code is good, however, to stick to the idea of composition and reusability, I would build an HOC that is more generic and don't put that much logic in it, then if you need something more specific, pass arguments or build another HOC on top of it.

例如:

const HOC = store => WrappedComponent => props => (
    // Render your wrapped component, spread the store to his props
);

您将 store 作为参数传递给 HOC 的地方,然后如果您需要它的特定切片,请将其指定为第一个参数或使用另一个 HOC 抽象.

Where you would pass the store as an argument to the HOC and then if you need a specific slice of it, either specify it as the first argument or use another HOC abstraction.

简而言之,与其使用将直接接受您包装的组件的 HOC,不如制作另一个将接受第一个参数的 HOC,这将为您提供更大的灵活性.第一个参数可以是 redux 存储、解构的 redux 存储、默认状态等,具体取决于您选择采用的逻辑.

In short, instead of using an HOC that will directly accept your wrapped component, make another HOC that will accept a first argument, it will give you more flexibility. This first argument could be the redux store, destructured redux store, the default state, etc depending on the logic you choose to go with.

  • Alternatively, recompose might also work for what you want.
  • Again, alternatively, you can make an HOC for redux's connect, have a look at this answer on SO

这篇关于ReactJS 中的高阶组件 - 从数据组件扩展功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆