React - 使用道具时重新渲染太多
React - Too many re-renders when using props
我一直在做一个在屏幕上显示图表的项目。现在,App.js 中的 data
状态作为 prop 传递,然后导致 infinite loop error
.
如果数据变量在 Graph.js 文件中并在 useState
中使用和定义,则没有问题。
我有点不确定为什么会这样,因为我对反应还比较陌生,而且 javascript。
目标是将数据添加到图表中,它会加载,然后当我更改 App.js
文件中的数据时,页面将自动再次加载新数据。
任何help/advice将不胜感激:)
App.js
function App() {
const [data, setData] = useState(null)
setData(
{
nodes:[
{name:"Max"},
{name:"George"},
{name:"Jesus"},
{name:"Ben"},
{name:"James"},
{name:"Sam"},
{name:"Sassms"}
],
edges:[
{source:"Max", target:"George"},
{source:"George", target:"Jesus"},
{source:"Jesus", target:"Max"},
{source:"Jesus", target:"Ben"},
{source:"James", target:"Ben"},
{source:"Sam", target:"Sassms"},
{source:"Sam", target:"Ben"}
]
})
console.log(data)
return (
<Graph colour="Grey" data={data} setData={setData}/>
);
}
Graph.js
function Graph(props) {
const svgRef = useRef(null);
useEffect(
() => {
let svg = d3.select(svgRef.current)
if(svgRef.current) {// Check that svg element has been rendered
let width = svg.attr("width")
let height = svg.attr("height")
d3.selectAll("svg > *").remove();
let edge = svg
.append("g")
.selectAll("line")
.data(props.data.edges)
.enter()
.append("line")
.attr("stroke-width", function(d) {
return 3;
})
.style("stroke", "black")
let node = svg
.append("g")
.selectAll("circle")
.data(props.data.nodes)
.enter()
.append("circle")
.attr("r", 7)
.attr("fill", function(d) {
return "grey";
})
.attr("stroke", "black")
}
}, [props.data])
return (
<svg ref={svgRef} id="svgID" width="1000" height="900"></svg>
)
}
这是因为您一次又一次地更新每个渲染器的状态。如果您只是简单地在函数内部调用 setData()
,它会调用此 setter 调用重新渲染并触发 React 再次调用您的 App
函数。
如果你想有一个默认状态,你可以像下面那样做。如果你想更新状态,应该通过其他动作来完成,比如点击一个按钮,获取一些数据等。
查看下面的演示,我在其中添加了一个按钮,用于向 nodes
数组添加新名称:
function App() {
const [data, setData] = React.useState({
nodes: [
{ name: 'Max' },
{ name: 'George' },
{ name: 'Jesus' },
{ name: 'Ben' },
{ name: 'James' },
{ name: 'Sam' },
{ name: 'Sassms' },
],
edges: [
{ source: 'Max', target: 'George' },
{ source: 'George', target: 'Jesus' },
{ source: 'Jesus', target: 'Max' },
{ source: 'Jesus', target: 'Ben' },
{ source: 'James', target: 'Ben' },
{ source: 'Sam', target: 'Sassms' },
{ source: 'Sam', target: 'Ben' },
],
});
return <Graph colour="Grey" data={data} setData={setData} />;
}
function Graph(props) {
function addRandomName() {
props.setData({
...props.data,
nodes: [
...props.data.nodes,
{ name: Math.random().toString(36).slice(2) },
],
});
}
return (
<React.Fragment>
<button onClick={addRandomName}>Add new name</button>
<h2>Names</h2>
<p> {props.data.nodes.map((d) => d.name).join(', ')}
</p>
<h2>State</h2>
<pre>{JSON.stringify(props.data, null, 2)}</pre>
</React.Fragment>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
我一直在做一个在屏幕上显示图表的项目。现在,App.js 中的 data
状态作为 prop 传递,然后导致 infinite loop error
.
如果数据变量在 Graph.js 文件中并在 useState
中使用和定义,则没有问题。
我有点不确定为什么会这样,因为我对反应还比较陌生,而且 javascript。
目标是将数据添加到图表中,它会加载,然后当我更改 App.js
文件中的数据时,页面将自动再次加载新数据。
任何help/advice将不胜感激:)
App.js
function App() {
const [data, setData] = useState(null)
setData(
{
nodes:[
{name:"Max"},
{name:"George"},
{name:"Jesus"},
{name:"Ben"},
{name:"James"},
{name:"Sam"},
{name:"Sassms"}
],
edges:[
{source:"Max", target:"George"},
{source:"George", target:"Jesus"},
{source:"Jesus", target:"Max"},
{source:"Jesus", target:"Ben"},
{source:"James", target:"Ben"},
{source:"Sam", target:"Sassms"},
{source:"Sam", target:"Ben"}
]
})
console.log(data)
return (
<Graph colour="Grey" data={data} setData={setData}/>
);
}
Graph.js
function Graph(props) {
const svgRef = useRef(null);
useEffect(
() => {
let svg = d3.select(svgRef.current)
if(svgRef.current) {// Check that svg element has been rendered
let width = svg.attr("width")
let height = svg.attr("height")
d3.selectAll("svg > *").remove();
let edge = svg
.append("g")
.selectAll("line")
.data(props.data.edges)
.enter()
.append("line")
.attr("stroke-width", function(d) {
return 3;
})
.style("stroke", "black")
let node = svg
.append("g")
.selectAll("circle")
.data(props.data.nodes)
.enter()
.append("circle")
.attr("r", 7)
.attr("fill", function(d) {
return "grey";
})
.attr("stroke", "black")
}
}, [props.data])
return (
<svg ref={svgRef} id="svgID" width="1000" height="900"></svg>
)
}
这是因为您一次又一次地更新每个渲染器的状态。如果您只是简单地在函数内部调用 setData()
,它会调用此 setter 调用重新渲染并触发 React 再次调用您的 App
函数。
如果你想有一个默认状态,你可以像下面那样做。如果你想更新状态,应该通过其他动作来完成,比如点击一个按钮,获取一些数据等。
查看下面的演示,我在其中添加了一个按钮,用于向 nodes
数组添加新名称:
function App() {
const [data, setData] = React.useState({
nodes: [
{ name: 'Max' },
{ name: 'George' },
{ name: 'Jesus' },
{ name: 'Ben' },
{ name: 'James' },
{ name: 'Sam' },
{ name: 'Sassms' },
],
edges: [
{ source: 'Max', target: 'George' },
{ source: 'George', target: 'Jesus' },
{ source: 'Jesus', target: 'Max' },
{ source: 'Jesus', target: 'Ben' },
{ source: 'James', target: 'Ben' },
{ source: 'Sam', target: 'Sassms' },
{ source: 'Sam', target: 'Ben' },
],
});
return <Graph colour="Grey" data={data} setData={setData} />;
}
function Graph(props) {
function addRandomName() {
props.setData({
...props.data,
nodes: [
...props.data.nodes,
{ name: Math.random().toString(36).slice(2) },
],
});
}
return (
<React.Fragment>
<button onClick={addRandomName}>Add new name</button>
<h2>Names</h2>
<p> {props.data.nodes.map((d) => d.name).join(', ')}
</p>
<h2>State</h2>
<pre>{JSON.stringify(props.data, null, 2)}</pre>
</React.Fragment>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>