React:是否可以在容器组件中调用更高阶的组件?

时间:2017-01-04 21:37:59

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

在我的代码库中,我有一个高阶组件(HOC),用于将所有输入验证功能添加到给定组件。当它在如此定义的组件上使用时效果很好......

let NameInput = React.createClass({
    render() {
        return (
            <div>
                <label htmlFor="name-input">Name</label>
                <input name="name-input" />
            </div>
        );
    }
});

let NameInput = addInputValidation(NameInput);

module.exports = NameInput;

但我现在需要根据服务器中的数组定义一系列输入。像这样......

let TestApp = React.createClass({
    render() {
        // Pretend the names array came from the server and is actually an array of objects.
        let names = ['First name', 'Middle name', 'Last name'];

        // Map over our names array in hopes of ending up with an array of input elements
        let nameComponents = names.map((name, index) => {
            let componentToRender = (
                    <div key={index}>
                        <label htmlFor={name}>{name}</label>
                        <input name={name} />
                    </div>
            );

            // Here is where I'd like to be able to use an HOC to wrap my name inputs with validation functions and stuff
            componentToRender = addInputValidation(componentToRender);

            return componentToRender;
        })


        return (
            <div>
                <p>Enter some names:</p>
                {nameComponents}
            </div>
        );
    }
})

let addInputValidation = function(Component) {
    let hoc = React.createClass({
        getInitialState() {
            return {
                isValid: false
            };
        },
        render() {
            return (
                <div>
                    <Component {...this.props} />
                    {this.state.isValid ? null : <p style={{color: "red"}}>Error!!!!</p>}
                </div>
            );
        }
    });

    return hoc;
}

module.exports = TestApp;

当你尝试渲染从另一个组件中调用HOC的结果时,React并不喜欢它。

我认为这与我的componentToRender不是真正的React组件或其他事实有关。

所以我的问题是......

为什么我不能从另一个组件中调用HOC?

有没有办法在数组的每个元素上调用HOC?

这里有一个可能有帮助的方面:https://jsfiddle.net/zt50r0wu/

编辑澄清一些事情:

我映射的数组实际上是一个描述输入细节的对象数组。包括输入类型(选择,复选框,文本等)。

此外,我的addInputValidation HOC实际上需要的参数多于组件。它需要从Redux存储中提取一系列存储索引以用于验证。这些存储索引是从描述输入的对象数组中的信息派生的。有权访问这个可能动态的数组是我希望能够在React生命周期内调用我的HOC的原因。

因此,对我的输入数组进行映射可能看起来更像......

let Select = require('components/presentational-form/select');
let Text = require('components/presentational-form/select');
let CheckboxGroup = require('components/presentational-form/select');
let TestApp = React.createClass({
    render() {
        // Pretend the inputs array came from the server
        let inputs = [{...}, {...}, {...}];
        // Map over our inputs array in hopes of ending up with an array of input objects
        let inputComponents = inputs.map((input, index) => {    
            let componentToRender = '';

            if (input.type === 'select') {
                componentToRender = <Select labelText={input.label} options={input.options} />;
            } else if (input.type === 'text') {
                componentToRender = <Text labelText={input.label} />;
            } else if (input.type === 'checkbox') {
                componentToRender = <CheckboxGroup labelText={input.label} options={input.options} />;
            }

            // Here is where I'd like to be able to use an HOC to wrap my name inputs with validation functions and stuff
            componentToRender = addInputValidation(componentToRender, input.validationIndexes);

            return componentToRender;
        })


        return (
            <div>
                <p>Enter some names:</p>
                {inputComponents}
            </div>
        );
    }
})

3 个答案:

答案 0 :(得分:1)

关于你的编辑:问题仍然是你要从.map回调中返回一个组件而不是一个元素。但这可以通过改变

轻松解决
return componentToRender;

return React.createElement(componentToRender);

您的代码中的问题是:

  • addInputValidation希望传递组件,但是你传递的是元素<div />)。
  • JSX希望传递一个(数组)元素,但是你传递的是一个组件数组。

解决问题的最简单方法似乎是创建一个通用的Input组件,它接受名称和值作为prop:

let Input = React.createClass({
    render() {
        return (
            <div>
                <label htmlFor={this.props.name}>{this.props.name}</label>
                <input name={this.props.name} />
            </div>
        );
    }
});

module.exports = addInputValidation(Input);

用作

let nameComponents = names.map((name, index) => <Input key={index} name={name} />);

答案 1 :(得分:1)

我认为你绊倒的是组件与元素之间的区别。我发现将组件视为函数并将元素视为该函数的结果是有帮助的。所以你真正要做的就是有条件地选择三个不同函数中的一个,传递一些参数,然后显示结果。我相信你想要这样的东西:

(这可以清理,顺便说一句,只是尽量保留你的代码结构)

r

答案 2 :(得分:1)

是的,当然有办法。但是HOC是处理验证的一种非常痛苦的方式。

There is a different approach to validation,基于ValueLink pattern

只需将生成的代码与高阶组件方法进行比较。