You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 12:45:02