fix(web): resolve React lint warnings for leaked timeouts, lazy state init, and key ordering

- Fix no-leaked-timeout: clean up setTimeout in toast provider and link editor hooks
- Fix prefer-use-state-lazy-initialization: use lazy initializers for useState with function calls
- Fix jsx-key-before-spread: place key prop before spread props in ErrorBoundary and agent node
This commit is contained in:
okxint 2026-03-19 20:33:38 +05:30
parent 11e1787100
commit fec2dda234
10 changed files with 25 additions and 15 deletions

View File

@ -37,7 +37,7 @@ export default function ChartView({ appId, headerRight }: IChartViewProps) {
const appDetail = useAppStore(state => state.appDetail)
const isChatApp = appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow'
const isWorkflow = appDetail?.mode === 'workflow'
const [period, setPeriod] = useState<PeriodParams>(IS_CLOUD_EDITION
const [period, setPeriod] = useState<PeriodParams>(() => IS_CLOUD_EDITION
? { name: t('filter.period.today', { ns: 'appLog' }), query: { start: today.startOf('day').format(queryDateFormat), end: today.endOf('day').format(queryDateFormat) } }
: { name: t('filter.period.last7days', { ns: 'appLog' }), query: { start: today.subtract(7, 'day').startOf('day').format(queryDateFormat), end: today.endOf('day').format(queryDateFormat) } },
)

View File

@ -180,7 +180,7 @@ const Configuration: FC = () => {
doSetCompletionParams(params)
}
const [modelConfig, doSetModelConfig] = useState<ModelConfig>({
const [modelConfig, doSetModelConfig] = useState<ModelConfig>(() => ({
provider: 'langgenius/openai/openai',
model_id: 'gpt-3.5-turbo',
mode: ModelModeType.unset,
@ -210,7 +210,7 @@ const Configuration: FC = () => {
},
dataSets: [],
agentConfig: DEFAULT_AGENT_SETTING,
})
}))
const isAgent = mode === AppModeEnum.AGENT_CHAT
const isOpenAI = modelConfig.provider === 'langgenius/openai/openai'

View File

@ -295,7 +295,7 @@ describe('Filter', () => {
const setQueryParams = vi.fn()
const Wrapper = () => {
const [queryParams, updateQueryParams] = useState<QueryParam>(createDefaultQueryParams())
const [queryParams, updateQueryParams] = useState<QueryParam>(() => createDefaultQueryParams())
const handleSetQueryParams = (next: QueryParam) => {
updateQueryParams(next)
setQueryParams(next)

View File

@ -210,8 +210,8 @@ const ErrorBoundary: React.FC<ErrorBoundaryProps> = (props) => {
return (
<ErrorBoundaryInner
{...props}
key={errorBoundaryKey}
{...props}
resetErrorBoundary={resetErrorBoundary}
onResetKeysChange={onResetKeysChange}
/>

View File

@ -99,9 +99,10 @@ export const ToastProvider = ({
useEffect(() => {
if (mounted) {
setTimeout(() => {
const timer = setTimeout(() => {
setMounted(false)
}, params.duration || defaultDuring)
return () => clearTimeout(timer)
}
}, [defaultDuring, mounted, params.duration])

View File

@ -41,7 +41,7 @@ const InstallFromGitHub: React.FC<InstallFromGitHubProps> = ({ updatePayload, on
handleStartToInstall,
} = useHideLogic(onClose)
const [state, setState] = useState<InstallState>({
const [state, setState] = useState<InstallState>(() => ({
step: updatePayload ? InstallStepFromGitHub.selectPackage : InstallStepFromGitHub.setUrl,
repoUrl: updatePayload?.originalPackageInfo?.repo
? convertRepoToUrl(updatePayload.originalPackageInfo.repo)
@ -49,7 +49,7 @@ const InstallFromGitHub: React.FC<InstallFromGitHubProps> = ({ updatePayload, on
selectedVersion: '',
selectedPackage: '',
releases: updatePayload ? updatePayload.originalPackageInfo.releases : [],
})
}))
const [uniqueIdentifier, setUniqueIdentifier] = useState<string | null>(null)
const [manifest, setManifest] = useState<PluginDeclaration | null>(null)
const [errorMsg, setErrorMsg] = useState<string | null>(null)

View File

@ -28,7 +28,7 @@ enum LogTypeEnum {
const LogViewer = ({ logs, className }: Props) => {
const { t } = useTranslation()
const [expandedLogs, setExpandedLogs] = useState<Set<string>>(new Set())
const [expandedLogs, setExpandedLogs] = useState<Set<string>>(() => new Set())
const toggleLogExpansion = (logId: string) => {
const newExpanded = new Set(expandedLogs)

View File

@ -105,8 +105,8 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
{models.map((model) => {
return (
<ModelBar
{...model}
key={model.param}
{...model}
/>
)
})}
@ -120,7 +120,7 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => {
)}
>
<div className="grid grid-cols-10 gap-0.5">
{tools.map((tool, i) => <ToolIcon {...tool} key={tool.id + i} />)}
{tools.map((tool, i) => <ToolIcon key={tool.id + i} {...tool} />)}
</div>
</Group>
)}

View File

@ -24,9 +24,12 @@ export const useOpenLink = () => {
const noteEditorStore = useNoteEditorStore()
useEffect(() => {
return mergeRegister(
let updateTimer: ReturnType<typeof setTimeout>
let clickTimer: ReturnType<typeof setTimeout>
const unregister = mergeRegister(
editor.registerUpdateListener(() => {
setTimeout(() => {
updateTimer = setTimeout(() => {
const {
selectedLinkUrl,
selectedIsLink,
@ -51,7 +54,7 @@ export const useOpenLink = () => {
editor.registerCommand(
CLICK_COMMAND,
(payload) => {
setTimeout(() => {
clickTimer = setTimeout(() => {
const {
selectedLinkUrl,
selectedIsLink,
@ -81,6 +84,12 @@ export const useOpenLink = () => {
COMMAND_PRIORITY_LOW,
),
)
return () => {
clearTimeout(updateTimer)
clearTimeout(clickTimer)
unregister()
}
}, [editor, noteEditorStore])
}

View File

@ -16,7 +16,7 @@ export default function CheckCode() {
const router = useRouter()
const searchParams = useSearchParams()
const email = decodeURIComponent(searchParams.get('email') as string)
const [token, setToken] = useState(decodeURIComponent(searchParams.get('token') as string))
const [token, setToken] = useState(() => decodeURIComponent(searchParams.get('token') as string))
const [code, setVerifyCode] = useState('')
const [loading, setIsLoading] = useState(false)
const locale = useLocale()