React Calendar Timeline仅在F12或窗口调整后渲染Items问题
react-calendar-timeline 异步加载items后不渲染,仅窗口调整/打开控制台后显示
问题现象
- groups数组可正常渲染,手动传入items值时组件显示正常
- 通过后端接口获取的items数据,仅在按下F12打开控制台或调整浏览器窗口后才会渲染到时间线上
- 已确认后端接口返回数据正常
相关代码
组件代码
import React, { useEffect, useState } from 'react' import Timeline from 'react-calendar-timeline' import 'react-calendar-timeline/lib/Timeline.css' import moment from 'moment' import { getProd } from '../services/frontendRoutes' export default function Production() { const [groups, setGroups] = useState([]); const [items, setItems] = useState([]); const fetchData = async () => { try { const { data } = await getProd(); setGroups(data.data.groups); const item = data.data.items; let itemss = item.map(i => { i.start_time = moment(i.start_time, 'YYYY-MM-DDTHH:mm:ss'); i.end_time = moment(i.end_time, 'YYYY-MM-DDTHH:mm:ss'); return i; }) setItems(itemss); } catch (err) { console.log(err) } } useEffect(() => { fetchData() }, []) return ( <div> <div> Production <Timeline groups={groups} items={items} defaultTimeStart={moment().startOf('year').toDate()} defaultTimeEnd={moment().endOf('year').toDate()} /> </div> </div> ) }
Axios请求代码
import axios from 'axios' const apiUrl_prod = 'http://localhost:8000/api/prod'; const apiUrl_sub = 'http://localhost:8000/api/sub' const apiUrl_paint = 'http://localhost:8000/api/paint' const apiUrl_final = 'http://localhost:8000/api/final' export function getProd() { return axios.get(apiUrl_prod); } export function getSub() { return axios.get(apiUrl_sub); } export function getPaint() { return axios.get(apiUrl_paint) } export function getFinal() { return axios.get(apiUrl_final); }
截图对比
- 按下F12前:

- 按下F12后:

解决方案
方案1:创建新对象避免引用复用
直接修改后端返回的原对象属性,会导致React浅比较无法检测到items数组变化。修改map逻辑,创建新对象而非修改原对象:
let itemss = item.map(i => ({ ...i, start_time: moment(i.start_time, 'YYYY-MM-DDTHH:mm:ss'), end_time: moment(i.end_time, 'YYYY-MM-DDTHH:mm:ss') }))
方案2:手动触发组件刷新
react-calendar-timeline在异步更新数据后可能不会自动重绘,通过ref调用组件的refresh方法强制刷新:
import React, { useEffect, useState, useRef } from 'react' export default function Production() { const [groups, setGroups] = useState([]); const [items, setItems] = useState([]); const timelineRef = useRef(null); const fetchData = async () => { try { const { data } = await getProd(); setGroups(data.data.groups); const item = data.data.items; let itemss = item.map(i => ({ ...i, start_time: moment(i.start_time, 'YYYY-MM-DDTHH:mm:ss'), end_time: moment(i.end_time, 'YYYY-MM-DDTHH:mm:ss') })) setItems(itemss); } catch (err) { console.log(err) } } useEffect(() => { fetchData() }, []) useEffect(() => { if (timelineRef.current && items.length > 0) { timelineRef.current.refresh(); } }, [items]) return ( <div> <div> Production <Timeline ref={timelineRef} groups={groups} items={items} defaultTimeStart={moment().startOf('year').toDate()} defaultTimeEnd={moment().endOf('year').toDate()} /> </div> </div> ) }
方案3:添加加载状态,确保数据就绪后渲染
添加loading状态,等待groups和items都加载完成后再渲染Timeline组件:
export default function Production() { const [groups, setGroups] = useState([]); const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const fetchData = async () => { try { const { data } = await getProd(); setGroups(data.data.groups); const item = data.data.items; let itemss = item.map(i => ({ ...i, start_time: moment(i.start_time, 'YYYY-MM-DDTHH:mm:ss'), end_time: moment(i.end_time, 'YYYY-MM-DDTHH:mm:ss') })) setItems(itemss); } catch (err) { console.log(err) } finally { setLoading(false); } } useEffect(() => { fetchData() }, []) if (loading) { return <div>加载中...</div>; } return ( <div> <div> Production <Timeline groups={groups} items={items} defaultTimeStart={moment().startOf('year').toDate()} defaultTimeEnd={moment().endOf('year').toDate()} /> </div> </div> ) }
内容的提问来源于stack exchange,提问作者Lakshay Saini
相关产品推荐
相关产品推荐

