【问题标题】:reactjs - expose react component methods outside react treereactjs - 在反应树之外公开反应组件方法
【发布时间】:2016-10-12 01:07:41
【问题描述】:

问题:

如何将 React 组件的方法暴露给其他地方?

例如,我想从 React 外部的元素调用 React-Router 的 this.context.router.push(location)。

也许我可以将 React 组件的方法添加到窗口对象,以便可以从任何通用 DOM 事件侦听器甚至控制台调用它?

背景/用例:

我想在我的 React 应用程序中使用 jQuery DataTables,因为它提供了许多在 React 生态系统中仍然不可用的插件和配置。

我从一个现有的 React 数据表组件开始(下面的实现)。

原始版本提供了很好的选项来传递渲染函数,例如,可以在单元格内渲染其他 React 组件。下面,“产品名称”列中的单元格呈现为 React-Router 组件。

    const data =  [
        { 
          product_id: '5001', 
          product_price: '$5', 
          product_name: 'Apple'
         },
         ...
      ];

    const renderUrl =
      (val, row) => {
        return (<Link to={`/product/${row.product_id}`}>{row.product_name}</Link>);
      };

    const columns = [
        { title: 'Product Name', prop: 'product_id', render: renderUrl },
        { title: 'Price', prop: 'product_price' },
      ];

    <DataTable
      className="datatable-container"
      columns={columns}
      initialData={data}
    />

我对现有组件所做的修改涉及对 React 的 DOM 差异算法隐藏表,否则当 jQuery DataTables 修改 DOM 时它会中断。

  1. 将组件的 render() 代码移动到类上的自定义方法 getDtMarkup() 中(在反应生命周期之外)。
  2. render() 现在输出一个带有 refid

    的空 div
      render() {
        return (
          <div>
            <div ref="dtContainer" id="dtContainer"></div>
          </div>
        );
      }
    
  3. componentDidMount 使用 ReactDomServer.renderToStaticMarkup 将 React 组件转换为普通的非反应标记,并将其附加到来自 render() 的 #dtContainer div。最后,jQuery DataTables 将呈现的表格 html 初始化为一个花哨的 'jQuery DataTable'。

    componentDidMount() {
    
      let table = this.getDTMarkup();
      let dtContainer = this.refs.dtContainer;
      let renderedTable = ReactDOMServer.renderToStaticMarkup(table, dtContainer);
    
      $('#dtContainer').append(renderedTable);
    
      let jqueryTable = $('#dt'); // hard coded in getDTMarkup() for now
    
      // Turn html table into a jQuery DataTable with desired config options
      jqueryTable.DataTable({
        dom: '<"html5buttons"B>lTfgitp',
        buttons: [
          'copy', 'csv', 'excel', 'pdf', 'print'
        ],
        "pagingType": 'numbers',
        "bAutoWidth": false,
        "bDestroy": true,
        "fnDrawCallback": function() {
          console.log('datatables fnDrawCallback');
        }
      });
    }
    

srchttps://github.com/alecperkey/react-jquery-datatables/blob/master/src/Table.js#L89-L111

让我问这个问题的限制是我现在无法在这个静态的非 React 标记中使用诸如 之类的 React 组件。我现在正在使用 ,但这会重新加载页面,速度较慢并导致浏览器出现白闪。

【问题讨论】:

    标签: jquery reactjs react-router react-dom


    【解决方案1】:

    这就是我设法将一个方法从一个反应组件公开到“全局”范围或反应应用程序之外的方法。 我真的不知道你的情况的具体情况,但这可能对你有用。另外,我使用了钩子,但这也应该适用于遗留的生命周期方法。

    想象这是我的组件。它只是呈现一个由状态管理的数字。

    const MyCount = ({ getMethods }) => {
      const [state, setState] = useState(1);
      useEffect(() => {
        getMethods({ setState });
      }, []);
      return <h1>{state}</h1>;
    }
    

    你看,getMethods 是这个工作的关键支柱。此函数将在组件安装时执行,并将提供我需要作为参数公开的方法。在这种情况下,setState 方法。

    现在让我们假设我在 react 之外有一个按钮,我想用它来触发该方法。

    // I want to store the methods in this variable
    let globalMethods;
    
    // When rendering the react component I pass the prop `getMethods` 
    // that will assign the returned value
    React.render(<MyCount getMethods={methods => globalMethods = methods} />, $someEl);
    
    // Now I can use it outside
    $("#myButton").click(() => {
      globalMethods.setState(2);
    })
    

    希望这会有所帮助。或者你甚至不再需要它,因为它已经晚了 3.9 年。

    【讨论】:

    • 它确实帮助了我,这是一个很棒的用法。我需要这个来从另一个域上的弹出窗口中获得 oauth 响应。
    【解决方案2】:

    有几种方法可以将 React 组件与“外部应用程序”连接起来

    您可以将方法作为道具传递给您的组件,例如:

    const foo = function(){
      alert(1)
    }
    
    class HelloWorldComponent extends React.Component {
      render() {
        return (      
          <h1 onClick={(e) => this.props.cb()}>Hello {this.props.name}</h1>      
        );
      }
    }
    
    React.render(
      <HelloWorldComponent cb={foo} name="Joe Schmoe"/>,
      document.getElementById('react_example')
    );
    

    http://jsbin.com/zujebirusa/1/edit?js,output

    使用附加到窗口的全局方法。请记住,这很难维护,因为它会污染全局命名空间。

    window.foo = function(){
      alert(1)
    }
    
    class HelloWorldComponent extends React.Component {
      render() {
        return (      
          <h1 onClick={(e) => window.foo()}>Hello {this.props.name}</h1>      
        );
      }
    }
    
    React.render(
      <HelloWorldComponent name="Joe Schmoe"/>,
      document.getElementById('react_example')
    );
    

    http://jsbin.com/woyokasano/1/edit?js,output

    使用 ES6 模块系统来保持代码库的整洁,并使用单独的范围

    //methods.js
    
    export function foo() {
        alert(1)
    }
    
    import {foo} from './methods';
    class HelloWorldComponent extends React.Component {
      render() {
        return (      
          <h1 onClick={(e) => foo()}>Hello {this.props.name}</h1>      
        );
      }
    }
    
    React.render(
      <HelloWorldComponent name="Joe Schmoe"/>,
      document.getElementById('react_example')
    );
    

    【讨论】:

    • 您向我展示了如何向 React 组件添加方法。不过,这不是我要问的。我要做的是将 React 组件的现有方法绑定到全局上下文(例如窗口)或非 React DOM 元素。 ...因此可以从通用 DOM 事件侦听器甚至控制台调用它。例如,如何将 React-Router 功能 this.context.router.push 绑定到 window 对象,以便我的非 React DOM 元素可以使用该功能?
    猜你喜欢
    • 2020-01-01
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 2016-11-02
    • 2021-09-24
    • 2017-08-24
    • 1970-01-01
    • 2021-11-12
    相关资源
    最近更新 更多