【问题标题】:Bind onClick when using higher order component使用高阶组件时绑定 onClick
【发布时间】:2018-04-20 21:21:01
【问题描述】:

可能是一个新手问题。我是新手。

根据一些博客文章等,我能够构建一个页面,该页面包含高阶组件和 componentDidMount 以从 API 加载数据并将其呈现到页面。它工作得很好,代码看起来很干净,但我不知道如何通过高阶组件传递某种 onClick,最终我想将 fetch 的内容移到一个可以调用的函数中componentDidMount<Button onClick={}>Reload</Button>。哈普请

import React, { Component } from 'react';
import {Button, CardColumns, Card, CardHeader, CardBody} from 'reactstrap';

const API = 'http://localhost:3000/';
const DEFAULT_QUERY = 'endpoint';

const withFetching = (url) => (Comp) =>
  class WithFetching extends Component {
    constructor(props) {
      super(props);

      this.state = {
        data: {},
        isLoading: false,
        error: null,
      };

      // Goes here?
      this.onClick = () => {
        console.log("Handled!");
      };
    }

    componentDidMount() {
      this.setState({ isLoading: true });

      fetch(url)
        .then(response => {
          if (response.ok) {
            return response.json();
          } else {
            throw new Error('Something went wrong ...');
          }
        })
        .then(data => this.setState({ data, isLoading: false }))
        .catch(error => this.setState({ error, isLoading: false }));
    }

    // Or here maybe??
    this.onClick = () => {
      console.log("Handled!");
    };

    render() {
      // How do I pass it in?
      return <Comp { ...this.props } { ...this.state } onClick={this.onClick} />
    }
  }

// How do I tell this component to expect it to recieve the handler?
const App = ({ data, isLoading, error }) => {
  const hits = data.hits || [];
  console.log(data);

  if (error) {
    return <p>{error.message}</p>;
  }

  if (isLoading) {
    return <p>Loading ...</p>;
  }

  return (
    <div className="animated fadeIn">
      <CardColumns className="cols-2">
        <Card>
          <CardHeader>
            API Card!
            <div className="card-actions">
            </div>
          </CardHeader>
          <CardBody>
            {hits.map(hit =>
              <div key={hit.foo}>
                <h3>{hit.foo}</h3>
                _____
              </div>
            )}

            <Button onClick={props.onClick}>Launch demo modal</Button>

          </CardBody>
        </Card>
      </CardColumns>
    </div>
  );
}

export default withFetching(API + DEFAULT_QUERY)(App);

Here 是引导我了解我正在使用的架构的博客文章:

编辑:我可以在类之外创建一个函数,以便它在任何地方都可用,但我最初想将它留在 in 的原因是我实际上可以更改状态并重新渲染带有新数据的卡。试图找出正确使用bind() 来完成这项工作...... JS 有时让我觉得很愚蠢:p

【问题讨论】:

  • 您可以将 onlcick 函数作为道具传递
  • 正确的语法是什么?
  • @mohhamad-hasham - 这似乎是正确的答案,但我无法坚持做对。有什么建议吗?
  • 你能说出哪里不对吗?这样我就可以帮助你!

标签: javascript reactjs higher-order-components


【解决方案1】:

您是否考虑过使该函数成为所有类之外的根级函数?然后任何组件都可以调用它。

例如:

import React, { Component } from 'react';
import {Button, CardColumns, Card, CardHeader, CardBody} from 'reactstrap';

const API = 'http://localhost:3000/';
const DEFAULT_QUERY = 'endpoint';

function sharedUtilityFunction(){
   // Do something here
}

const withFetching = (url) => (Comp) =>
  class WithFetching extends Component {
    constructor(props) {
      super(props);

      this.state = {
        data: {},
        isLoading: false,
        error: null,
      };

    }

    componentDidMount() {

      sharedUtilityFunction();

      this.setState({ isLoading: true });

      fetch(url)
        .then(response => {
          if (response.ok) {
            return response.json();
          } else {
            throw new Error('Something went wrong ...');
          }
        })
        .then(data => this.setState({ data, isLoading: false }))
        .catch(error => this.setState({ error, isLoading: false }));
    }

    render() {
      return <Comp { ...this.props } { ...this.state } />
    }
  }

// How do I tell this component to expect it to recieve the handler?
const App = ({ data, isLoading, error }) => {
  const hits = data.hits || [];
  console.log(data);

  if (error) {
    return <p>{error.message}</p>;
  }

  if (isLoading) {
    return <p>Loading ...</p>;
  }

  return (
    <div className="animated fadeIn">
      <CardColumns className="cols-2">
        <Card>
          <CardHeader>
            API Card!
            <div className="card-actions">
            </div>
          </CardHeader>
          <CardBody>
            {hits.map(hit =>
              <div key={hit.foo}>
                <h3>{hit.foo}</h3>
                _____
              </div>
            )}

            <Button onClick={() => sharedUtilityFunction()}>Launch demo modal</Button>

          </CardBody>
        </Card>
      </CardColumns>
    </div>
  );
}

export default withFetching(API + DEFAULT_QUERY)(App);

【讨论】:

  • 我对此持开放态度。快速示例?
  • @msanteler 将函数移到类外。 function onClick()....
  • 非常感谢。这似乎应该工作!这不会在全球范围内公开它或任何类似的东西,对吧?
  • 另外,这个根函数能否与状态交互,以便我可以this.setState({ isLoading: true }); 等?
  • “全局”范围的代码在不同的上下文中可能意味着不同的东西。看起来您正在使用导入,所以我通常做的是创建一个包含这些函数的“utilities.js”文件并根据需要导入它们。如果要调用this.setState,只需将this.setState函数作为参数传入函数并在函数内部调用,或者[绑定函数](developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…)
【解决方案2】:

我正在寻找的答案确实是将函数作为道具传递给低阶组件。为此,我需要将 compent 预期 args 的方式更改为:const App = props =&gt; { TBD 无论这是否有其他影响,但我 认为 状态已经被传递了一个 prop 无论如何......正确调用该函数会导致 isLoading 渲染,这是一个好兆头。

import React, { Component } from 'react';
import {Button, CardColumns, Card, CardHeader, CardBody} from 'reactstrap';

const API = 'http://localhost:3000/';
const DEFAULT_QUERY = 'endpoint';

const withFetching = (url) => (Comp) =>
  class WithFetching extends Component {
    constructor(props) {
      super(props);

      this.state = {
        data: {},
        isLoading: false,
        error: null,
      };

      this.goFetch = this.goFetch.bind(this);
    }

    goFetch() {
      this.setState({ isLoading: true });
      fetch(url)
        .then(response => {
          if (response.ok) {
            return response.json();
          } else {
            throw new Error('Something went wrong ...');
          }
        })
        .then(data => this.setState({ data, isLoading: false }))
        .catch(error => this.setState({ error, isLoading: false }));
    }

    componentDidMount() {
      this.goFetch();
    }

    render() {
      return <Comp { ...this.props } { ...this.state } goFetch={this.goFetch}/>
    }
  }

const App = props => {
  const hits = props.data.hits || [];

  if (props.error) {
    return <p>{props.error.message}</p>;
  }

  if (props.isLoading) {
    return <p>Loading ...</p>;
  }

  return (
    <div className="animated fadeIn">
      <CardColumns className="cols-2">
        <Card>
          <CardHeader>
            API Card!
            <div className="card-actions">
            </div>
          </CardHeader>
          <CardBody>
            {hits.map((hit, index) =>
              <div key={index}>
                Foo: <h3>{hit.foo}</h3>
              </div>
            )}
            <Button onClick={props.goFetch}>Refresh</Button>
          </CardBody>
        </Card>
      </CardColumns>
    </div>
  );
}



export default withFetching(API + DEFAULT_QUERY)(App);

【讨论】:

  • 这个应该是正确的。也许一个重构是使用像goFetch = () =&gt; {...} 这样的粗箭头定义goFetch ,这样我们就不需要在构造函数中做this.goFetch = tihs.goFetch.bind(this)。原因 ES6 胖箭头函数在调用时会保留此上下文。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-16
  • 2018-05-23
  • 2020-02-06
  • 2020-01-01
  • 2017-09-26
  • 1970-01-01
  • 2017-10-18
相关资源
最近更新 更多