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

react-calendar-timeline的itemRenderer与useState Hook兼容问题求助

问题分析

react-calendar-timeline 的 itemRenderer 在旧版本实现中是作为普通函数调用,而非React组件实例化,导致React Hook无法在其中正常使用(Hook仅允许在组件或自定义Hook的顶层调用)。以下是无需类组件的解决方案:


解决方案

方案1:状态提升到父组件

将自定义项的状态转移到使用<Timeline>的父组件中,通过props传递状态及更新方法,是最直接的兼容方案:

1. 父组件维护状态

import { useState } from 'react';
import Timeline from 'react-calendar-timeline';
import CustomItemRenderer from './CustomItemRenderer';

const ParentComponent = () => {
  // 用对象存储每个item的自定义状态,key为item的唯一id
  const [itemCustomStates, setItemCustomStates] = useState({});

  // 更新指定item的状态
  const updateItemState = (itemId, newState) => {
    setItemCustomStates(prev => ({
      ...prev,
      [itemId]: newState
    }));
  };

  // 现有逻辑:groups、items、defaultTimeStart/End等定义

  return (
    <Timeline
      items={items}
      groups={groups}
      defaultTimeStart={defaultTimeStart}
      defaultTimeEnd={defaultTimeEnd}
      // 传递状态和更新方法给自定义渲染器
      itemRenderer={(props) => (
        <CustomItemRenderer
          {...props}
          someCustomState={itemCustomStates[props.item.id] ?? null}
          setSomeCustomState={(val) => updateItemState(props.item.id, val)}
        />
      )}
    />
  );
};

2. 修改自定义渲染器

const CustomItemRenderer = ({ item, itemContext, getItemProps, someCustomState, setSomeCustomState }) => {
  // 直接使用父组件传递的状态和更新方法
  return (
    <div 
      {...getItemProps(item.itemProps)} 
      onClick={() => setSomeCustomState('状态已更新')}
    >
      Custom {itemContext.title} - 当前状态:{someCustomState ?? '未设置'}
    </div>
  );
};

export default CustomItemRenderer;

方案2:使用React Context封装状态

如果多个组件需要访问这些项的状态,用Context统一管理更便于维护:

1. 创建状态Context

// ItemStateContext.js
import { createContext, useContext, useState, useCallback } from 'react';

const ItemStateContext = createContext();

export const ItemStateProvider = ({ children }) => {
  const [itemStates, setItemStates] = useState({});

  const updateItemState = useCallback((itemId, value) => {
    setItemStates(prev => ({ ...prev, [itemId]: value }));
  }, []);

  return (
    <ItemStateContext.Provider value={{ itemStates, updateItemState }}>
      {children}
    </ItemStateContext.Provider>
  );
};

export const useItemState = () => useContext(ItemStateContext);

2. 在应用外层包裹Provider

// App.jsx
import { ItemStateProvider } from './ItemStateContext';
import ParentComponent from './ParentComponent';

const App = () => {
  return (
    <ItemStateProvider>
      <ParentComponent />
    </ItemStateProvider>
  );
};

3. 在自定义渲染器中使用Context

// CustomItemRenderer.jsx
import { useItemState } from './ItemStateContext';

const CustomItemRenderer = ({ item, itemContext, getItemProps }) => {
  const { itemStates, updateItemState } = useItemState();
  const someCustomState = itemStates[item.id] ?? null;
  const setSomeCustomState = (val) => updateItemState(item.id, val);

  return (
    <div 
      {...getItemProps(item.itemProps)} 
      onClick={() => setSomeCustomState('通过Context更新')}
    >
      Custom {itemContext.title} - 当前状态:{someCustomState ?? '未设置'}
    </div>
  );
};

export default CustomItemRenderer;

方案3:尝试修复Hook直接使用问题

若库内部支持渲染JSX元素(而非直接调用函数),可修改itemRenderer的传递方式,让CustomItemRenderer作为真正的组件被渲染,此时即可直接使用Hook:

修改Timeline的itemRenderer配置

<Timeline
  // 其他属性
  itemRenderer={(props) => <CustomItemRenderer {...props} />}
/>

自定义渲染器直接使用Hook

import { useState } from 'react';

const CustomItemRenderer = ({ item, itemContext, getItemProps }) => {
  const [someCustomState, setSomeCustomState] = useState(null);

  return (
    <div 
      {...getItemProps(item.itemProps)} 
      onClick={() => setSomeCustomState('Hook正常工作')}
    >
      Custom {itemContext.title} - 当前状态:{someCustomState ?? '未设置'}
    </div>
  );
};

export default CustomItemRenderer;

注意:此方案仅在库内部通过React渲染itemRenderer返回的元素时有效,若库是直接调用函数生成DOM则无法生效。


内容的提问来源于stack exchange,提问作者JBrown

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 22:35:19