让React组件每分钟重新渲染一次的正确方法是什么?

问题描述 投票:0回答:1

我正在使用 TypeScript 开发一个 React-Native 项目,我面临一个问题,我希望组件每分钟重新渲染一次,即使从选择器派生的状态没有改变。由于

dateProvider.getNow()
函数,我的选择器的输出发生了变化。

这是我的组件的简化版本:

import { useSelector } from 'react-redux';
import { useEffect, useState } from 'react';

export function HomeScreen({ navigation }) {
  const viewModel = useSelector((rootState) => selectHomeViewModel(rootState, dateProvider))

  // rest of the component code
}

homeViewModel:

import { RootState } from '../../../../core/_redux_/createStore.ts'
import {
  BlockSession,
  blockSessionAdapter,
} from '../../../../core/block-session/block.session.ts'
import { formatDistance, isBefore } from 'date-fns'
import { createSelector } from '@reduxjs/toolkit'
import {
  Greetings,
  HomeViewModel,
  HomeViewModelType,
  SessionBoardMessage,
  SessionBoardTitle,
} from './home-view-model.types.ts'
import { DateProvider } from '../../../../infra/date-provider/port.date-provider.ts'

function greetUser(now: Date) {
  const hour = now.getHours()

  if (hour >= 6 && hour < 12) return Greetings.GoodMorning
  if (hour >= 12 && hour < 18) return Greetings.GoodAfternoon
  if (hour >= 18 && hour < 22) return Greetings.GoodEvening
  return Greetings.GoodNight
}

function generateTimeInfo(now: Date, start: Date, end: Date) {
  return isActive(now, start, end)
    ? 'Ends ' +
        formatDistance(end, now, {
          addSuffix: true,
        })
    : 'Starts at ' +
        start.getHours().toString().padStart(2, '0') +
        ':' +
        start.getMinutes().toString().padStart(2, '0')
}

function recoverDate(now: Date, time: string) {
  const [hours, minutes] = time.split(':').map(Number)

  const todayWithNewTime = new Date(now.getTime())
  todayWithNewTime.setHours(hours)
  todayWithNewTime.setMinutes(minutes)

  return todayWithNewTime
}

function formatToViewModel(blockSessions: BlockSession[], now: Date) {
  return blockSessions.map((session) => {
    const start = recoverDate(now, session.start)
    const end = recoverDate(now, session.end)

    const timeline = generateTimeInfo(now, start, end)

    return {
      id: session.id,
      name: session.name,
      minutesLeft: timeline,
      blocklists: session.blocklists.length,
      devices: session.devices.length,
    }
  })
}

function isActive(now: Date, start: Date, end: Date) {
  return !isBefore(now, start) && isBefore(now, end)
}

export const selectHomeViewModel = createSelector(
  [
    (rootState: RootState) => rootState.blockSession,
    (_state: RootState, dateProvider: DateProvider) => dateProvider,
  ],
  (blockSession, dateProvider): HomeViewModelType => {
    const blockSessions = blockSessionAdapter
      .getSelectors()
      .selectAll(blockSession)

    const greetings = greetUser(dateProvider.getNow())

    const NO_ACTIVE_SESSION = {
      title: SessionBoardTitle.NO_ACTIVE_SESSIONS as const,
      message: SessionBoardMessage.NO_ACTIVE_SESSIONS as const,
    }

    const NO_SCHEDULED_SESSION = {
      title: SessionBoardTitle.NO_SCHEDULED_SESSIONS as const,
      message: SessionBoardMessage.NO_SCHEDULED_SESSIONS as const,
    }

    if (!blockSessions.length)
      return {
        type: HomeViewModel.WithoutActiveNorScheduledSessions,
        greetings,
        activeSessions: NO_ACTIVE_SESSION,
        scheduledSessions: NO_SCHEDULED_SESSION,
      }

    const activeSessions = blockSessions.filter((session) => {
      const now = dateProvider.getNow()
      const start = recoverDate(now, session.start)
      const end = recoverDate(now, session.end)
      return isActive(now, start, end)
    })
    const formattedActiveSessions = formatToViewModel(
      activeSessions,
      dateProvider.getNow(),
    )

    const scheduledSessions = blockSessions.filter((session) => {
      const now = dateProvider.getNow()
      const start = recoverDate(now, session.start)
      const end = recoverDate(now, session.end)
      return !isActive(now, start, end)
    })
    const formattedScheduledSessions = formatToViewModel(
      scheduledSessions,
      dateProvider.getNow(),
    )

    if (!activeSessions.length)
      return {
        type: HomeViewModel.WithoutActiveWithScheduledSessions,
        greetings,
        activeSessions: NO_ACTIVE_SESSION,
        scheduledSessions: {
          title: SessionBoardTitle.SCHEDULED_SESSIONS,
          blockSessions: formattedScheduledSessions,
        },
      }

    if (!scheduledSessions.length)
      return {
        type: HomeViewModel.WithActiveWithoutScheduledSessions,
        greetings,
        activeSessions: {
          title: SessionBoardTitle.ACTIVE_SESSIONS,
          blockSessions: formattedActiveSessions,
        },
        scheduledSessions: NO_SCHEDULED_SESSION,
      }

    return {
      type: HomeViewModel.WithActiveAndScheduledSessions,
      greetings,
      activeSessions: {
        title: SessionBoardTitle.ACTIVE_SESSIONS as const,
        blockSessions: formattedActiveSessions,
      },
      scheduledSessions: {
        title: SessionBoardTitle.SCHEDULED_SESSIONS as const,
        blockSessions: formattedScheduledSessions,
      },
    }
  },
)

这是日期提供者:

export class RealDateProvider implements DateProvider {
  getISOStringNow(): string {
    return new Date().toISOString()
  }

  getNow(): Date {
    return new Date()
  }
}

我尝试将

useEffect
setInterval
一起使用来强制每分钟重新渲染一次,但我无法使其工作,并且我不确定这是否是最好的方法或者是否有其他方法可以做到这一点.

reactjs typescript react-hooks react-redux
1个回答
0
投票

我不同意强制重新渲染组件的需要或用例比所需的频率更频繁即当状态或道具值发生变化时),但如果这是你想做的事情那么我相信你尝试使用

useEffect
钩子和间隔计时器,走在正确的道路上。

尝试以下实现:

// Some state to update to trigger component rerenders.
const [, setRerender] = React.useState(0);

// Mounting effect to instantiate 1-minute interval to
// force rerender component.
React.useEffect(() => {
  // Handler to enqueue a local state update to trigger a rerender
  const forceRerender = () => setRerender(c => c + 1);

  // Instantiated interval to trigger state update
  const timerId = setInterval(forceRerender, 60000);

  // Cleanup function to clear any running timers
  // when the component unmounts.
  return () => {
    clearInterval(timerId);
  };
}, []);
© www.soinside.com 2019 - 2024. All rights reserved.