我正在使用 React 18 并在 3 个不同级别使用
ReactDOM.render
。第一级位于根级,我很清楚并使用以下代码替换了它:
import { createRoot } from 'react-dom/client';
const root = createRoot(domElement);
root.render(reactElement);
对于其他两个级别(根的子级),我想在指定的 DOM 元素中渲染某个组件。如果我正在使用:
import { createRoot } from 'react-dom/client';
const root = createRoot(childDomElement);
root.render(reactElement);
我收到以下警告:
您正在对之前已传递给 createRoot() 的容器调用 ReactDOMClient.createRoot() 。相反,如果您想更新它,请在现有根上调用 root.render() 。
在特定 DOM 元素中渲染组件的正确方法是什么?
我也遇到过这种情况。对我来说,这是因为
DOMContentLoaded
回调触发了两次。
我的修复只是确保容器仅渲染一次。
let container = null;
document.addEventListener('DOMContentLoaded', function(event) {
if (!container) {
container = document.getElementById('root1') as HTMLElement;
const root = createRoot(container)
root.render(
<React.StrictMode>
<h1>ZOO</h1>
</React.StrictMode>
);
}
});
您可能从入口点文件导入某些内容,导致入口点文件以某种方式运行两次。我遇到了同样的问题,并通过确保我没有从入口点文件导入任何内容来解决它。
可能还有其他人在使用
'react-router-dom'
包时遇到与我相同的错误。
删除
<App />
(或主组件)组件,并在 <RouterProvider />
函数内调用 .render()
组件。像这样:
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
应替换为
ReactDOM.createRoot(document.getElementById('root')).render(<RouterProvider router={mainRouter} />)
当您在同一页面上同时调用
<RouterProvider>
和 .createRoot
时,就会出现问题。要解决此问题,您应该导入并调用 <RouterProvider>
或 <App>
组件来解决此问题,而不是直接渲染 <RouterProvider>
组件。
触发此警告的典型方法是在同一根上多次调用
createRoot
:
ReactDOM.createRoot(document.querySelector("#app"))
.render(<p>A</p>);
ReactDOM.createRoot(document.querySelector("#app"))
.render(<p>B</p>);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
您可以看到
B
渲染覆盖了 A
渲染。
如果你想运行两个不同的 React 应用程序,请使用不同的根:
ReactDOM.createRoot(document.querySelector("#app-a"))
.render(<p>A</p>);
ReactDOM.createRoot(document.querySelector("#app-b"))
.render(<p>B</p>);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app-a"></div>
<div id="app-b"></div>
否则,如果您尝试在同一应用程序中渲染不同的组件,请使用单个根组件渲染一次,并在根的子树内渲染子组件。这是常见的情况;大多数页面都使用一个 React 应用程序。
在大多数情况下,
createRoot
充当整个 React 应用程序的一次性设置,在用户访问页面期间持续存在。渲染条件位于组件树内部,并且要渲染的元素在组件中指定为 JSX。
例如:
const A = () => <p>A</p>;
const B = () => <p>B</p>;
const App = () => <React.Fragment><A /><B /></React.Fragment>;
ReactDOM.createRoot(document.querySelector("#app"))
.render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
如果出于某种原因,您想多次渲染该根,只要您限制自己为每个根调用一次
createRoot
即可:
const root = ReactDOM.createRoot(document.querySelector("#app"));
root.render(<p>A</p>);
// silly example
setTimeout(() => root.render(<p>B</p>), 2000);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
另一种可能性是您正在寻找一个门户,可以让您逃离 React DOM 树并在其他地方渲染组件。不过,如上所述,仍然有一个
createRoot
和一个 render
,我们使用门户将组件放入特定元素中。
例如:
const A = () => <p>A</p>;
const B = () => ReactDOM.createPortal(
<p>B</p>,
document.querySelector("#portal")
);
const App = () => <React.Fragment><A /><B /></React.Fragment>;
ReactDOM.createRoot(document.querySelector("#app"))
.render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
<div id="portal"></div>
门户经常出现在模态中。
为了完整起见,触发此问题的另一种不常见方法是包含 Babel 两次,这可能发生在具有
babel: true
以及导入的 Stack Snippet 中:
ReactDOM.createRoot(document.querySelector("#app"))
.render(<p>test</p>);
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
这或多或少与以下相同:
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>
<script type="text/babel">
ReactDOM.createRoot(document.querySelector("#app"))
.render(<p>test</p>);
</script>
我遇到了这个问题,我正在使用 Rollup 捆绑一个复杂的 NPM 包,该包与我们的 Storybook 应用程序和 Create-React-App 演示页面位于同一目录中。
事实证明,我们正在捆绑 Create-React-App index.js 文件,其中已经包含 createRoot() 了。这意味着它试图将包中的 React 应用程序与我们的 React 应用程序一起附加。
我通过不捆绑我们用来演示组件的 index.js 文件来解决这个问题,并制作了一个不包含 createRoot() 的“package-index.js”文件,专门用于该包。
希望这对某人有帮助:)
我有类似的情况,我必须在当前组件中单击按钮时渲染另一个组件。我通过以下代码成功实现了这一点。
import React from "react";
import ReactDOM from "react-dom/client";
import reportWebVitals from "./reportWebVitals";
class Lifecycle extends React.Component{
showReports=()=>{
root.render(<Reports></Reports>);
}
render() {
return (
<div>
<h2>Welcome to Component Lifecycle..</h2>
<button onClick={this.showReports}>Show Reports</button>
</div>
);
}
}
class Report extends React.Component{
render() {
return (
<div>
<h2>Welcome to Report Component..</h2>
</div>
);
}
}
const element=<Lifecycle></Lifecycle>;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element);
reportWebVitals();
我通过一个愚蠢的错误解决了这个问题,旧参数仍然存在:
const root = createRoot(childDomElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
最后一个不应该在那里
document.getElementById('root'),
答案就在警告本身中。
您正在一个 container 上调用 ReactDOMClient.createRoot() ,该容器具有 已经被传递给 createRoot() before。
我最后出现警告的根本原因是同一个 DOM 元素多次用于创建根。
为了解决这个问题,需要确保
createRoot
仅在 一个 DOM 元素上调用一次,之后 root.render(reactElement);
可以用于更新,并且 createRoot
不应该被使用。