在 React 不直接管理 DOM 的区域,可以在浏览器中使用 ReactDOMServer.renderToString 吗?
Is it ok to use ReactDOMServer.renderToString in the browser in areas where React isn't directly managing the DOM?
我正在使用 Leaflet (via react-leaflet) 开发应用程序。 Leaflet直接操作DOM。 react-leaflet 库不会改变这一点,它只是为您提供 React 组件,您可以使用这些组件以 React 友好的方式控制您的 Leaflet 地图。
在此应用中,我想使用自定义地图标记,它们是包含一些简单元素的 div。在 Leaflet 中这样做的方法是将标记的 icon
属性 设置为 DivIcon,您可以在其中设置自定义 HTML。您可以通过将 DivIcon 的 html
属性 设置为包含 HTML 的字符串来设置内部 HTML。就我而言,我希望 HTML 从 React 组件呈现。
为了做到这一点,似乎正确的方法是使用 ReactDOMServer.renderToString()
将我想要的地图标记内的组件呈现为一个字符串,然后我将其设置为 html
属性 的 DivIcon:
MyMarker.js:
import React, { Component } from 'react'
import { renderToString } from 'react-dom/server'
import { Marker } from 'react-leaflet'
import { divIcon } from 'leaflet'
import MarkerContents from './MarkerContents'
export class MyMarker extends Component {
render() {
const markerContents = renderToString(<MarkerContents data={this.props.data} />)
const myDivIcon = divIcon({
className: 'my-marker',
html: markerContents
})
return (
<Marker
position={this.props.position}
icon={myDivIcon} />
)
}
}
然而,根据React docs:
This [renderToString] should only be used on the server.
这是一条严格的规则,还是只是为了劝阻人们规避 ReactDOM 对 DOM 的高效管理?
我想不出另一种(更好的)方法来完成我所追求的。如有任何意见或想法,我们将不胜感激。
我知道这个问题太老了,但由于还没有人回答我想分享我的想法。
我使用的是相同的东西,renderToString
,但是由于文档建议不要在客户端使用它,我通过使用 react-dom
的 render
方法将自定义组件呈现为 div
var myDiv = document.createElement('div');
ReactDOM.render(
<MarkerContents data={this.props.data} />,
myDiv
);
var myIcon = L.divIcon({
iconSize: new L.Point(50, 50),
html: myDiv.innerHTML
});
根据新文档:https://reactjs.org/docs/react-dom-server.html
The following methods can be used in both the server and browser
environments:
- renderToString()
- renderToStaticMarkup()
正如 Thomas 所说,是的,您可以在客户端上使用 renderToString
。不过要明确一点,您需要在客户端上导入 ReactDOMServer
,这看起来 counter-intuitive 但似乎是正确的。示例(在客户端):
import React from 'react';
import ReactDOMServer from 'react-dom/server';
const MyComp = (props) => {
const html = ReactDOMServer.renderToString(<div>{someFunc(props)}</div>);
// do something with your html, then
return <div dangerouslySetInnerHTML={{__html: html}}></div>;
};
我遇到了与传单完全相同的问题,最终通过 thinking with portals 解决了问题。
import React, { useEffect, useMemo,useState } from "react";
import ReactDOM from "react-dom";
import { useLeafletContext } from "@react-leaflet/core";
import * as L from "leaflet";
/**
* @type { React.FC<{
* positionX?: number
* positionY?: number
* width?: number
* height?:number
* }> }
* */
const LeafletChild = ({ children, positionX=0, positionY=0, width, height }) => {
const context = useLeafletContext();
const [divElement] = useState(() => document.createElement("div"));
const icon = useMemo(() => L.divIcon({ iconSize: new L.Point(height, width), html: divElement }), [height,width, divElement]);
const marker = useMemo(() => L.marker([0,0], { icon }), [icon]);
useEffect(()=>{
marker.setLatLng([positionY, positionX])
},[positionY,positionX, marker])
useEffect(() => {
const container = context.layerContainer || context.map;
container.addLayer(marker);
return () => container.removeLayer(marker);
}, [context, marker]);
return ReactDOM.createPortal(children, divElement);
};
其他地方...
<LeafletChild>
<div onClick={()=>setSomeBool(!someBool)}>
anything you want here like...{`${someBool}`}
</div>
</LeafletChild>
我正在使用 Leaflet (via react-leaflet) 开发应用程序。 Leaflet直接操作DOM。 react-leaflet 库不会改变这一点,它只是为您提供 React 组件,您可以使用这些组件以 React 友好的方式控制您的 Leaflet 地图。
在此应用中,我想使用自定义地图标记,它们是包含一些简单元素的 div。在 Leaflet 中这样做的方法是将标记的 icon
属性 设置为 DivIcon,您可以在其中设置自定义 HTML。您可以通过将 DivIcon 的 html
属性 设置为包含 HTML 的字符串来设置内部 HTML。就我而言,我希望 HTML 从 React 组件呈现。
为了做到这一点,似乎正确的方法是使用 ReactDOMServer.renderToString()
将我想要的地图标记内的组件呈现为一个字符串,然后我将其设置为 html
属性 的 DivIcon:
MyMarker.js:
import React, { Component } from 'react'
import { renderToString } from 'react-dom/server'
import { Marker } from 'react-leaflet'
import { divIcon } from 'leaflet'
import MarkerContents from './MarkerContents'
export class MyMarker extends Component {
render() {
const markerContents = renderToString(<MarkerContents data={this.props.data} />)
const myDivIcon = divIcon({
className: 'my-marker',
html: markerContents
})
return (
<Marker
position={this.props.position}
icon={myDivIcon} />
)
}
}
然而,根据React docs:
This [renderToString] should only be used on the server.
这是一条严格的规则,还是只是为了劝阻人们规避 ReactDOM 对 DOM 的高效管理?
我想不出另一种(更好的)方法来完成我所追求的。如有任何意见或想法,我们将不胜感激。
我知道这个问题太老了,但由于还没有人回答我想分享我的想法。
我使用的是相同的东西,renderToString
,但是由于文档建议不要在客户端使用它,我通过使用 react-dom
的 render
方法将自定义组件呈现为 div
var myDiv = document.createElement('div');
ReactDOM.render(
<MarkerContents data={this.props.data} />,
myDiv
);
var myIcon = L.divIcon({
iconSize: new L.Point(50, 50),
html: myDiv.innerHTML
});
根据新文档:https://reactjs.org/docs/react-dom-server.html
The following methods can be used in both the server and browser environments:
- renderToString()
- renderToStaticMarkup()
正如 Thomas 所说,是的,您可以在客户端上使用 renderToString
。不过要明确一点,您需要在客户端上导入 ReactDOMServer
,这看起来 counter-intuitive 但似乎是正确的。示例(在客户端):
import React from 'react';
import ReactDOMServer from 'react-dom/server';
const MyComp = (props) => {
const html = ReactDOMServer.renderToString(<div>{someFunc(props)}</div>);
// do something with your html, then
return <div dangerouslySetInnerHTML={{__html: html}}></div>;
};
我遇到了与传单完全相同的问题,最终通过 thinking with portals 解决了问题。
import React, { useEffect, useMemo,useState } from "react";
import ReactDOM from "react-dom";
import { useLeafletContext } from "@react-leaflet/core";
import * as L from "leaflet";
/**
* @type { React.FC<{
* positionX?: number
* positionY?: number
* width?: number
* height?:number
* }> }
* */
const LeafletChild = ({ children, positionX=0, positionY=0, width, height }) => {
const context = useLeafletContext();
const [divElement] = useState(() => document.createElement("div"));
const icon = useMemo(() => L.divIcon({ iconSize: new L.Point(height, width), html: divElement }), [height,width, divElement]);
const marker = useMemo(() => L.marker([0,0], { icon }), [icon]);
useEffect(()=>{
marker.setLatLng([positionY, positionX])
},[positionY,positionX, marker])
useEffect(() => {
const container = context.layerContainer || context.map;
container.addLayer(marker);
return () => container.removeLayer(marker);
}, [context, marker]);
return ReactDOM.createPortal(children, divElement);
};
其他地方...
<LeafletChild>
<div onClick={()=>setSomeBool(!someBool)}>
anything you want here like...{`${someBool}`}
</div>
</LeafletChild>