如何让dateRangePicker有效修改上层组件及useAnalytics中的状态
React日期范围选择器与useAnalytics状态关联实现方案
我们可以通过状态提升+受控组件的方式关联两者,同时可利用SWR缓存特性避免重复请求,具体实现步骤如下:
第一步:修改useAnalytics,暴露状态修改方法
首先将自定义Hook内的起止日期状态、修改方法导出,供上层组件调用,可选添加SWR缓存配置避免相同参数重复请求:
import useSWR from 'swr'; import { useMemo, useState, useEffect } from "react"; import { apiRequest } from '../../../util/util'; import { format } from "date-fns"; import groupBy from "lodash/groupBy"; export default function useAnalytics() { var d = new Date(); d.setMonth(d.getMonth() - 3); const [EndDate, setEndDate] = useState((Date.now())); const [StartDate, setStartDate] = useState(d.getTime()); const { data: magicData } = useSWR( `xxx/xxx/xx-xx-xxx?startTime=${StartDate}&endTime=${EndDate}`, apiRequest, // 可选配置:相同参数的请求直接读取缓存,不重新发起请求 { revalidateIfStale: false, revalidateOnFocus: false } ); return { magicData, StartDate, EndDate, setStartDate, setEndDate }; }
第二步:修改DateRange组件,支持受控模式
把类组件改为支持上层传入初始值和回调的受控模式,选中日期后通过props回调将结果传给上层:
import React from 'react'; import moment from 'moment'; import Helmet from 'react-helmet'; import DayPickerInput from 'react-day-picker/DayPickerInput'; import 'react-day-picker/lib/style.css'; import { formatDate, parseDate } from 'react-day-picker/moment'; export default class DateRange extends React.Component { constructor(props) { super(props); this.handleFromChange = this.handleFromChange.bind(this); this.handleToChange = this.handleToChange.bind(this); // 初始值优先从props取,兼容默认场景 this.state = { from: props.initialFrom || undefined, to: props.initialTo || undefined, }; } showFromMonth() { const { from, to } = this.state; if (!from) { return; } if (moment(to).diff(moment(from), 'months') < 2) { this.to.getDayPicker().showMonth(from); } } handleFromChange(from) { this.setState({ from }); // 选中起始日期后触发上层回调 if (this.props.onFromChange) this.props.onFromChange(from); // 选中起始日期后自动聚焦结束日期选择框 if (from) this.to.getInput().focus(); } handleToChange(to) { this.setState({ to }, this.showFromMonth); // 选中结束日期后触发上层回调 if (this.props.onToChange) this.props.onToChange(to); } render() { const { from, to } = this.state; const modifiers = { start: from, end: to }; return ( <div className="InputFromTo"> <DayPickerInput value={from} placeholder="From" format="LL" formatDate={formatDate} parseDate={parseDate} dayPickerProps={{ selectedDays: [from, { from, to }], disabledDays: { after: to }, toMonth: to, modifiers, numberOfMonths: 2, }} onDayChange={this.handleFromChange} />{' '} —{' '} <span className="InputFromTo-to"> <DayPickerInput ref={el => (this.to = el)} value={to} placeholder="To" format="LL" formatDate={formatDate} parseDate={parseDate} dayPickerProps={{ selectedDays: [from, { from, to }], disabledDays: { before: from }, modifiers, month: from, fromMonth: from, numberOfMonths: 2, }} onDayChange={this.handleToChange} /> </span> <Helmet> <style>{` .InputFromTo .DayPicker-Day--selected:not(.DayPicker-Day--start):not(.DayPicker-Day--end):not(.DayPicker-Day--outside) { background-color: #f0f8ff !important; color: #4a90e2; } .InputFromTo .DayPicker-Day { border-radius: 0 !important; } .InputFromTo .DayPicker-Day--start { border-top-left-radius: 50% !important; border-bottom-left-radius: 50% !important; } .InputFromTo .DayPicker-Day--end { border-top-right-radius: 50% !important; border-bottom-right-radius: 50% !important; } .InputFromTo .DayPickerInput-Overlay { width: 550px; } .InputFromTo-to .DayPickerInput-Overlay { margin-left: -198px; } `}</style> </Helmet> </div> ); } }
第三步:在Analytics父组件中完成关联
在父组件中拿到useAnalytics导出的状态和方法,传给DateRange组件完成双向绑定:
import React from 'react'; import moment from 'moment'; import useAnalytics from './useAnalytics.js' import DateRange from './DateRange'; export default function Analytics() { const { magicData, StartDate, EndDate, setStartDate, setEndDate } = useAnalytics(); // 同步起始日期到useAnalytics const handleFromChange = (fromDate) => { if (fromDate) setStartDate(moment(fromDate).valueOf()); } // 同步结束日期到useAnalytics const handleToChange = (toDate) => { if (toDate) setEndDate(moment(toDate).valueOf()); } return ( <div className="relative"> <DateRange initialFrom={moment(StartDate).toDate()} initialTo={moment(EndDate).toDate()} onFromChange={handleFromChange} onToChange={handleToChange} /> <LineGraphs sentimentData={sentimentData} /> </div> ); }
额外优化:如果要完全避免频繁请求,可以固定SWR的查询范围为最大时间跨度(比如近1年),拿到全量数据后在前端根据选中的日期范围做过滤,不需要触发任何后端请求。
内容的提问来源于stack exchange,提问作者LeCoda
相关产品推荐
相关产品推荐

