将道具传递给子组件时避免组件重新渲染
Avoid component re-rendering when passing props to child component
我是 React 的新手,现在我被困住了。基本上我从 API 获取数据并递归地用标记填充 MAPBOX。我有一个 App
组件,我从中获取数据,一个 Map
组件,我在其中设置地图及其所有属性,然后我有 InfoHeader
组件来显示更多数据我从API.
抓取
我将存储在状态中的数据传递给 InfoHeader
的方式是在单击地图中的标记之一后通过道具。
当前一期:
单击标记后,我将重新呈现整个 Map
组件,因此弹出窗口永远不会显示,即使数据已正确传递给 InfoHeader
,我也不希望 Map
重新显示渲染,仅 InfoHeader
我试过使用 React.memo,但到目前为止没有成功。
代码
Map.js
import React, {useEffect, useRef, useState} from 'react'
import mapboxgl from 'mapbox-gl'
import InfoHeader from './InfoHeader'
mapboxgl.accessToken = "pk.eyJ1IjoibGVob3VkaW5pIiwiYSI6ImNram43b3FhZTNzYWUydnNjd21zcmJ1d2QifQ.ouLhKn6B6pzmbZJtymQIcg"
const Map = React.memo(({data}) => {
const mapRef = useRef(null)
useEffect(() => {
const map = new mapboxgl.Map({
container: mapRef.current,
style: 'mapbox://styles/mapbox/dark-v10',
center: [-104.9876, 39.7405],
zoom: 1.5,
})
//Marker logic in async func
addMarkerToMap(data, map)
// add navigation control (the +/- zoom buttons)
map.addControl(new mapboxgl.NavigationControl(), 'bottom-right');
return () => {
//cleanup when unmounting
map.remove()
}
})
//state to be passed to InfoHeader
const [population, setPopulation] = useState()
const [infected, setInfected] = useState()
const [recovered, setRecovered] = useState()
const addMarkerToMap = async (mapData, map) => {
if(Object.keys(mapData).length) {
mapData.forEach(el => {
const {countryInfo, country, cases, population, recovered, deaths} = el
const {lat, long, flag} = countryInfo
console.log(el)
const marker = new mapboxgl.Marker()
.setLngLat([long, lat])
.setPopup(new mapboxgl.Popup().setHTML(
`<img style="width: 100%" src=${flag} alt="flag"/> <h1 style="text-align:center; color: #fcfcfc">${country}</h1>
<p>Population: ${population}</p>
<p>Number of cases: ${cases}</p>
<p>Deaths: ${deaths}
<p>Recovered: ${recovered}</p>
`))
.addTo(map)
marker.getElement().addEventListener('click', () => {
setPopulation(population)
setInfected(cases)
setRecovered(recovered)
})
})
}
}
return(
<div className="map-wrapper">
<InfoHeader
population = {population}
infected = {infected}
recovered = {recovered}
/>
<div className="map-container" ref={mapRef}>
</div>
</div>
)
})
export default Map
InfoHeader.js
import React from 'react'
const InfoHeader = ({population, infected, recovered}) => {
return(
<div className="infoHeader-wrapper">
<div className="infoHeader-population">
<h2 className="info-title">Population</h2>
<p className="info-data">{population}</p>
</div>
<div className="infoHeader-infected">
<h2 className="info-title">Infected</h2>
<p className="info-data">{infected}</p>
</div>
<div className="infoHeader-recovered">
<h2 className="info-title">Recovered</h2>
<p className="info-data">{recovered}</p>
</div>
</div>
)
}
export default InfoHeader
任何帮助都会很棒,即使它是指向一些资源来阅读更多关于我做错的事情。谢谢!
为避免您的 useEffect
挂钩再次重新运行,添加一个空数组作为第二个参数。
来自 React.js 在 useEffect
上的文档
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.
useEffect(() => {
const map = new mapboxgl.Map({
container: mapRef.current,
style: 'mapbox://styles/mapbox/dark-v10',
center: [-104.9876, 39.7405],
zoom: 1.5,
})
//Marker logic in async func
addMarkerToMap(data, map)
// add navigation control (the +/- zoom buttons)
map.addControl(new mapboxgl.NavigationControl(), 'bottom-right');
return () => {
//cleanup when unmounting
map.remove()
}
}, [])
我是 React 的新手,现在我被困住了。基本上我从 API 获取数据并递归地用标记填充 MAPBOX。我有一个 App
组件,我从中获取数据,一个 Map
组件,我在其中设置地图及其所有属性,然后我有 InfoHeader
组件来显示更多数据我从API.
我将存储在状态中的数据传递给 InfoHeader
的方式是在单击地图中的标记之一后通过道具。
当前一期:
单击标记后,我将重新呈现整个 Map
组件,因此弹出窗口永远不会显示,即使数据已正确传递给 InfoHeader
,我也不希望 Map
重新显示渲染,仅 InfoHeader
我试过使用 React.memo,但到目前为止没有成功。
代码
Map.js
import React, {useEffect, useRef, useState} from 'react'
import mapboxgl from 'mapbox-gl'
import InfoHeader from './InfoHeader'
mapboxgl.accessToken = "pk.eyJ1IjoibGVob3VkaW5pIiwiYSI6ImNram43b3FhZTNzYWUydnNjd21zcmJ1d2QifQ.ouLhKn6B6pzmbZJtymQIcg"
const Map = React.memo(({data}) => {
const mapRef = useRef(null)
useEffect(() => {
const map = new mapboxgl.Map({
container: mapRef.current,
style: 'mapbox://styles/mapbox/dark-v10',
center: [-104.9876, 39.7405],
zoom: 1.5,
})
//Marker logic in async func
addMarkerToMap(data, map)
// add navigation control (the +/- zoom buttons)
map.addControl(new mapboxgl.NavigationControl(), 'bottom-right');
return () => {
//cleanup when unmounting
map.remove()
}
})
//state to be passed to InfoHeader
const [population, setPopulation] = useState()
const [infected, setInfected] = useState()
const [recovered, setRecovered] = useState()
const addMarkerToMap = async (mapData, map) => {
if(Object.keys(mapData).length) {
mapData.forEach(el => {
const {countryInfo, country, cases, population, recovered, deaths} = el
const {lat, long, flag} = countryInfo
console.log(el)
const marker = new mapboxgl.Marker()
.setLngLat([long, lat])
.setPopup(new mapboxgl.Popup().setHTML(
`<img style="width: 100%" src=${flag} alt="flag"/> <h1 style="text-align:center; color: #fcfcfc">${country}</h1>
<p>Population: ${population}</p>
<p>Number of cases: ${cases}</p>
<p>Deaths: ${deaths}
<p>Recovered: ${recovered}</p>
`))
.addTo(map)
marker.getElement().addEventListener('click', () => {
setPopulation(population)
setInfected(cases)
setRecovered(recovered)
})
})
}
}
return(
<div className="map-wrapper">
<InfoHeader
population = {population}
infected = {infected}
recovered = {recovered}
/>
<div className="map-container" ref={mapRef}>
</div>
</div>
)
})
export default Map
InfoHeader.js
import React from 'react'
const InfoHeader = ({population, infected, recovered}) => {
return(
<div className="infoHeader-wrapper">
<div className="infoHeader-population">
<h2 className="info-title">Population</h2>
<p className="info-data">{population}</p>
</div>
<div className="infoHeader-infected">
<h2 className="info-title">Infected</h2>
<p className="info-data">{infected}</p>
</div>
<div className="infoHeader-recovered">
<h2 className="info-title">Recovered</h2>
<p className="info-data">{recovered}</p>
</div>
</div>
)
}
export default InfoHeader
任何帮助都会很棒,即使它是指向一些资源来阅读更多关于我做错的事情。谢谢!
为避免您的 useEffect
挂钩再次重新运行,添加一个空数组作为第二个参数。
来自 React.js 在 useEffect
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.
useEffect(() => {
const map = new mapboxgl.Map({
container: mapRef.current,
style: 'mapbox://styles/mapbox/dark-v10',
center: [-104.9876, 39.7405],
zoom: 1.5,
})
//Marker logic in async func
addMarkerToMap(data, map)
// add navigation control (the +/- zoom buttons)
map.addControl(new mapboxgl.NavigationControl(), 'bottom-right');
return () => {
//cleanup when unmounting
map.remove()
}
}, [])