Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/mercurial/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
JavaScript/ReactJS中多态性的FP替代方案_Javascript_Reactjs_Ecmascript 6_Functional Programming - Fatal编程技术网

JavaScript/ReactJS中多态性的FP替代方案

JavaScript/ReactJS中多态性的FP替代方案,javascript,reactjs,ecmascript-6,functional-programming,Javascript,Reactjs,Ecmascript 6,Functional Programming,我目前正在做一个ReactJS项目,我需要创建“可重用”组件,其中一些方法需要“重写”。在OOP中,我将使用多态性。我读了一些书,似乎大家都同意使用HoC/composition,但我不太明白如何实现这一点。我想,如果我可以使用composition获得一个ES6样本,那么之后将想法应用于ReactJS可能会更容易 下面是一个ES6 OOP示例(忽略事件处理,这只是为了测试),它几乎是我希望在ReactJS中实现的。是否有人对如何将ReactJS组件分解成一个HoC组件有一些指导,或者仅仅演示如

我目前正在做一个ReactJS项目,我需要创建“可重用”组件,其中一些方法需要“重写”。在OOP中,我将使用多态性。我读了一些书,似乎大家都同意使用HoC/composition,但我不太明白如何实现这一点。我想,如果我可以使用composition获得一个ES6样本,那么之后将想法应用于ReactJS可能会更容易

下面是一个ES6 OOP示例(忽略事件处理,这只是为了测试),它几乎是我希望在ReactJS中实现的。是否有人对如何将ReactJS组件分解成一个HoC组件有一些指导,或者仅仅演示如何根据示例在ES6中使用composition

类传输组件{
构造函数(){
让timeout=null;
这个。render();
此为.events();
}
事件(){
让范围=这个;
document.getElementById('button')。addEventListener('click',function(){
范围。验证。应用(范围);
});
}
验证(){
if(this.isValid()){
这个是.ajax();
}
}
isValid(){
if(document.getElementById('username').value!=''){
返回true;
}
返回false;
}
ajax(){
clearTimeout(this.timeout);
document.getElementById('message').textContent='Loading…';
this.timeout=setTimeout(函数(){
document.getElementById('message').textContent='Success';
}, 500);
}
render(){
document.getElementById('content')。innerHTML='\n\
验证';
}
}
类重写TransferComponent扩展TransferComponent{
isValid(){
if(document.getElementById('username').value!=''&&document.getElementById('password').value!=''){
返回true;
}
返回false;
}
render(){
document.getElementById('content')。innerHTML='\n\
\n\
验证';
}
}
const overrideTransferComponent=新的overrideTransferComponent()


阅读您的问题,不清楚您指的是组合还是继承,但它们是不同的OOP概念。如果你不知道它们之间的区别,我建议你看看

关于您的具体问题,请回复。我建议您尝试使用用户组合,因为它为您构建UI和传递道具提供了很大的灵活性

例如,如果您正在使用React,则在动态填充对话框时可能已经在使用合成。如图所示:

您可以导入它们并在组件中使用。

非反应FP示例 首先,在函数式编程中,函数是一等公民。这意味着您可以像对待OOP中的数据一样对待函数(即作为参数传递、分配给变量等)

您的示例将数据与对象中的行为混合在一起。为了编写一个纯功能性的解决方案

函数式编程从根本上讲就是将数据与行为分离

那么,让我们从
isValid
开始

函数是有效的 这里有几种方法可以对逻辑进行排序,但我们将使用以下方法:

  • 给出了一个ID列表
  • 如果不存在无效id,则所有id均有效
  • 在JS中,这可以翻译为:

    const areAllElementsValid = (...ids) => !ids.some(isElementInvalid)
    
    我们需要一对助手函数来实现这一点:

    const isElementInvalid = (id) => getValueByElementId(id) === ''
    const getValueByElementId = (id) => document.getElementById(id).value
    
    我们可以把所有的内容都写在一行上,但是把它分解会让它更可读。这样,我们就有了一个通用函数,可以用来确定组件的
    isValid

    areAllElementsValid('username') // TransferComponent.isValid
    areAllElementsValid('username', 'password') // TransferOverrideComponent.isValid
    
    功能渲染 我在
    isValid
    上用
    document
    作弊了一点。在真正的函数式编程中,函数应该是纯函数。或者,换句话说,函数调用的结果只能由其输入确定(也就是说,它是幂等的),并且不能有

    那么,我们如何在没有副作用的情况下渲染DOM呢?React为核心库使用虚拟DOM(一种驻留在内存中的奇特数据结构,通过传入函数和从函数返回来保持函数的纯度)。React的副作用存在于
    React dom
    库中

    对于我们的例子,我们将使用超级简单的虚拟DOM(类型为
    string

    这可能看起来过于简单,根本不起作用。但是
    +
    操作符实际上是功能性的!想想看:

    • 它接受两个输入(左操作数和右操作数)
    • 它返回一个结果(对于字符串,是操作数的串联)
    • 它没有副作用
    • 它不改变输入(结果是一个新字符串——操作数不变)
    因此,
    render
    现在可以正常工作了

    那ajax呢? 不幸的是,在没有副作用的情况下,我们无法执行ajax调用、修改DOM、设置事件侦听器或设置超时。我们可以通过复杂的方式为这些操作创建monad,但是出于我们的目的,我们只需要继续使用非函数方法就足够了

    在React中应用 下面是使用常见反应模式对示例的重写。我正在使用表单输入。我们所讨论的大多数函数概念实际上都是在React中实现的,所以这是一个非常简单的实现,不使用任何花哨的东西

    class Form extends React.Component {
        constructor(props) {
            super(props);
    
            this.state = {
                loading: false,
                success: false
            };
        }
    
        handleSubmit() {
            if (this.props.isValid()) {
                this.setState({
                    loading: true
                });
    
                setTimeout(
                    () => this.setState({
                        loading: false,
                        success: true
                    }),
                    500
                );
            }
        }
    
        render() {
            return (
                <div>
                    <form onSubmit={this.handleSubmit}>
                        {this.props.children}
                        <input type="submit" value="Submit" />
                    </form>
    
                    { this.state.loading && 'Loading...' }
                    { this.state.success && 'Success' }
                </div>
            );
        }
    }
    
    您也可以编写另一个
    表单
    ,但输入不同

    class CommentForm extends React.Component {
        constructor(props) {
            super(props);
    
            this.state = {
                comment: ''
            };
        }
    
        isValid() {
            return this.state.comment !== '';
        }
    
        handleCommentChange(event) {
            this.setState({ comment: event.target.value });
        }
    
        render() {
            return (
                <Form
                    validate={this.isValid}
                >
                    <input value={this.state.comment} onChange={this.handleCommentChange} />
                </Form>
            );
        }
    }
    
    最后,我们使用
    ReactDOM
    而不是
    innerHTML

    ReactDOM.render(
        <App />,
        document.getElementById('content')
    );
    
    ReactDOM.render(
    很好地覆盖这个


    为了进一步阅读,James K.Nelson收集了一些关于React的重要资源,这些资源应该有助于您理解功能:

    关于示例代码的答案在本文的中间/底部

    去abo的好方法
    USERNAME_INPUT + VALIDATE_BUTTON // TransferComponent.render
    USERNAME_INPUT + PASSWORD_INPUT + VALIDATE_BUTTON // TransferOverrideComponent.render
    
    class Form extends React.Component {
        constructor(props) {
            super(props);
    
            this.state = {
                loading: false,
                success: false
            };
        }
    
        handleSubmit() {
            if (this.props.isValid()) {
                this.setState({
                    loading: true
                });
    
                setTimeout(
                    () => this.setState({
                        loading: false,
                        success: true
                    }),
                    500
                );
            }
        }
    
        render() {
            return (
                <div>
                    <form onSubmit={this.handleSubmit}>
                        {this.props.children}
                        <input type="submit" value="Submit" />
                    </form>
    
                    { this.state.loading && 'Loading...' }
                    { this.state.success && 'Success' }
                </div>
            );
        }
    }
    
    class LoginForm extends React.Component {
        constructor(props) {
            super(props);
    
            this.state = {
                username: '',
                password: ''
            };
        }
    
        isValid() {
            return this.state.username !== '' && this.state.password !== '';
        }
    
        handleUsernameChange(event) {
            this.setState({ username: event.target.value });
        }
    
        handlePasswordChange(event) {
            this.setState({ password: event.target.value });
        }
    
        render() {
            return (
                <Form
                    validate={this.isValid}
                >
                    <input value={this.state.username} onChange={this.handleUsernameChange} />
                    <input value={this.state.password} onChange={this.handlePasswordChange} />
                </Form>
            );
        }
    }
    
    class CommentForm extends React.Component {
        constructor(props) {
            super(props);
    
            this.state = {
                comment: ''
            };
        }
    
        isValid() {
            return this.state.comment !== '';
        }
    
        handleCommentChange(event) {
            this.setState({ comment: event.target.value });
        }
    
        render() {
            return (
                <Form
                    validate={this.isValid}
                >
                    <input value={this.state.comment} onChange={this.handleCommentChange} />
                </Form>
            );
        }
    }
    
    class App extends React.Component {
        render() {
            return (
                <div>
                    <LoginForm />
                    <CommentForm />
                </div>
            );
        }
    }
    
    ReactDOM.render(
        <App />,
        document.getElementById('content')
    );
    
    const App = () => <Delegator ImplementationComponent={ImplementationB} />;
    
    class Delegator extends React.Component {
      render() {
        const { ImplementationComponent } = this.props;
    
        return (
          <div>
            <ImplementationComponent>
              { ({ doLogic }) => {
                /* ... do/render things based on doLogic ... */
              } }
            </ImplementationComponent>
          </div>
        );
      }
    }
    
    class ImplementationA extends React.Component {
    
      doSomeLogic() { /* ... variation A ... */ }
    
      render() {
        this.props.children({ doLogic: this.doSomeLogic })
      }
    }
    
    class ImplementationB extends React.Component {
    
      doSomeLogic() { /* ... variation B ... */ }
    
      render() {
        this.props.children({ doLogic: this.doSomeLogic })
      }
    } 
    
    class Delegator extends React.Component {
      render() {
        const { ImplementationComponent, AnotherImplementation, SomethingElse } = this.props;
    
        return (
          <div>
            <ImplementationComponent>
              { ({ doLogic }) => { /* ... */} }
            </ImplementationComponent>
    
            <AnotherImplementation>
              { ({ doThings, moreThings }) => { /* ... */} }
            </AnotherImplementation>
    
            <SomethingElse>
              { ({ foo, bar }) => { /* ... */} }
            </SomethingElse>
          </div>
        );
      }
    }
    
    const App = () => (
      <div>
        <Delegator 
          ImplementationComponent={ImplementationB}
          AnotherImplementation={AnotherImplementation1}
          SomethingElse={SomethingVariationY}
        />
    
        <Delegator 
          ImplementationComponent={ImplementationC}
          AnotherImplementation={AnotherImplementation2}
          SomethingElse={SomethingVariationZ}
        />
      </div>
    ); 
    
    <div id="content-inputs"></div>
    <div id="content-button"></div> 
    
    export default class TransferComponent extends React.Component {
      constructor() {
        super();
        this.displayDOMButton = this.displayDOMButton.bind(this);
        this.onButtonPress = this.onButtonPress.bind(this);
      }
    
      ajax(){
        console.log('doing some ajax')
      }
    
      onButtonPress({ isValid }) {
        if (isValid()) {
          this.ajax();
        }
      }
    
      displayDOMButton({ isValid }) {
        document.getElementById('content-button').innerHTML = (
          '<button id="button" type="button">Validate</button>'
        );
    
        document.getElementById('button')
          .addEventListener('click', () => this.onButtonPress({ isValid }));
      }
    
      render() {
        const { VaryingComponent } = this.props;
        const { displayDOMButton } = this;
    
        return (
          <div>
            <VaryingComponent>
              {({ isValid, displayDOMInputs }) => {
                displayDOMInputs();
                displayDOMButton({ isValid });
                return null;
              }}
            </VaryingComponent>
          </div>
        )
      }
    };
    
    export default class UsernameComponent extends React.Component {
      isValid(){
        return document.getElementById('username').value !== '';
      }
    
      displayDOMInputs() {
        document.getElementById('content-inputs').innerHTML = (
          '<input type="text" id="username" value="username"/>'
        );
      }
    
      render() {
        const { isValid, displayDOMInputs } = this;
    
        return this.props.children({ isValid, displayDOMInputs });
      }
    }
    
    export default class UsernamePasswordComponent extends React.Component {
      isValid(){
        return (
          document.getElementById('username').value !== '' &&
          document.getElementById('password').value !== ''
        );
      }
    
      displayDOMInputs() {
        document.getElementById('content-inputs').innerHTML = (
          '<input type="text" id="username" value="username"/>\n\
          <input type="text" id="password" value="password"/>\n'
        );
      }
    
      render() {
        const { isValid, displayDOMInputs } = this;
    
        return this.props.children({ isValid, displayDOMInputs });
      }
    }
    
    <TransferComponent VaryingComponent={UsernameComponent} />
    <TransferComponent VaryingComponent={UsernamePasswordComponent} />