问题
Question:
How can I expose a react component's methods to other places?
For example, I want to call React-Router's this.context.router.push(location) from an element outside of React.
Perhaps I could add a React component's method to the window object so it can be called from any generic DOM event listener or even the console?
Background/Use Case:
I want to use jQuery DataTables in my React app because it provides many plugins and config which are still unavailable in the React ecosystem.
I started from an existing React datatable component (implementation below).
The original provides nice option to pass a render function which can, for example, render other React components inside the cells. Below, the cells in the 'Product Name' column are rendered as React-Router < Link /> components.
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}
/>
What I've done to modify the existing component involves hiding the table from React's DOM diffing algorithm, since it would otherwise break when jQuery DataTables modifies the DOM.
- Move the component's render() code into a custom method getDtMarkup() on the class (outside of the react lifecycle).
render() now outputs an empty div with a ref and id
render() { return ( <div> <div ref="dtContainer" id="dtContainer"></div> </div> ); }
componentDidMount uses ReactDomServer.renderToStaticMarkup to turn a React component into plain, non-react markup and appends this to the #dtContainer div from render(). Finally jQuery DataTables initializes the rendered table html as a fancy '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
The limitation that has me asking this question is that I am now unable to use React components such as < Link /> inside of this static, non-React markup. I am using < a href=""> for now, but this will reload the page, which is slower and causes the white flash of the browser.
回答1:
There are several ways to wire up React components with the "outer application"
You can pass methods as props to your component like:
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
Using global methods attached to the window. Keep in mind that's hard to maintain as it will pollute the global namespace.
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
Using ES6 module system in order to keep your codebase tidy with separate scopes
//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')
);
来源:https://stackoverflow.com/questions/37758834/reactjs-expose-react-component-methods-outside-react-tree