4

问题:

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

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

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

背景/用例:

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

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

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

    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');
        }
      });
    }
    

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

我提出这个问题的限制是我现在无法在这个静态的非 React 标记中使用 React 组件,例如 < Link />。我现在正在使用 <a href="">,但这会重新加载页面,速度较慢并导致浏览器出现白色闪烁。

4

2 回答 2

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 年。

于 2020-02-11T02:08:21.233 回答
0

有几种方法可以将 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,输出

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

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,输出

使用 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')
);
于 2016-06-11T09:41:30.623 回答