Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions packages/components/hooks/useMutationObserver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef, useEffect } from 'react';
import { useEffect, useRef } from 'react';
import { debounce, isEqual } from 'lodash-es';
import useLatest from './useLatest';

Expand All @@ -17,17 +17,26 @@ type Options = typeof DEFAULT_OPTIONS;
export default function useMutationObservable(
targetEl: HTMLElement | null,
cb: MutationCallback,
options = DEFAULT_OPTIONS,
options?: Partial<Options>,
) {
const optionsRef = useRef<Options>(null);
const signalRef = useRef(0);
const callbackRef = useLatest(cb);

if (!isEqual(options, optionsRef.current)) {
// 合并用户配置和默认配置
const mergedOptions: Options = {
debounceTime: options?.debounceTime ?? DEFAULT_OPTIONS.debounceTime,
config: {
...DEFAULT_OPTIONS.config,
...options?.config,
},
};

if (!isEqual(mergedOptions, optionsRef.current)) {
signalRef.current += 1;
}

optionsRef.current = options;
optionsRef.current = mergedOptions;

useEffect(() => {
if (!targetEl || !targetEl?.nodeType) return;
Expand Down
32 changes: 24 additions & 8 deletions packages/components/radio/RadioGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React, { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react';
import classNames from 'classnames';

import observe from '@tdesign/common-js/utils/observe';
import { CheckContext, type CheckContextValue } from '../common/Check';
import useCommonClassName from '../hooks/useCommonClassName';
import useConfig from '../hooks/useConfig';
import { TdRadioGroupProps } from './type';
import useControlled from '../hooks/useControlled';
import useCommonClassName from '../hooks/useCommonClassName';
import { StyledProps } from '../common';
import { CheckContext, CheckContextValue } from '../common/Check';
import useDefaultProps from '../hooks/useDefaultProps';
import useMutationObserver from '../hooks/useMutationObserver';
import Radio from './Radio';
import { radioGroupDefaultProps } from './defaultProps';
import useDefaultProps from '../hooks/useDefaultProps';
import useKeyboard from './useKeyboard';

import type { StyledProps } from '../common';
import type { TdRadioGroupProps } from './type';

/**
* RadioGroup 组件所接收的属性
*/
Expand All @@ -26,11 +29,11 @@ const RadioGroup: React.FC<RadioGroupProps> = (originalProps) => {
const { classPrefix } = useConfig();

const props = useDefaultProps<RadioGroupProps>(originalProps, radioGroupDefaultProps);

const { disabled, readonly, children, onChange, size, variant, options = [], className, style, theme } = props;

const [internalValue, setInternalValue] = useControlled(props, 'value', onChange);
const [barStyle, setBarStyle] = useState<Partial<CSSProperties> | null>(null);

const radioGroupRef = useRef<HTMLDivElement>(null);
const observerRef = useRef<IntersectionObserver>(null);

Expand Down Expand Up @@ -81,19 +84,32 @@ const RadioGroup: React.FC<RadioGroupProps> = (originalProps) => {
});
};

// 针对子元素更新的场景,包括 value 变化等
useMutationObserver(radioGroupRef.current, (mutations) => {
// 排除高亮元素自身的变化,避免重复触发
const filteredMutations = mutations.filter((mutation) => {
const target = mutation.target as HTMLElement;
return !target.classList?.contains(`${classPrefix}-radio-group__bg-block`);
});

if (filteredMutations.length > 0) {
calcBarStyle();
}
});

useEffect(() => {
calcBarStyle();

if (!radioGroupRef.current) return;

// 针对父元素初始化时隐藏导致无法正确计算尺寸的问题
const observer = observe(radioGroupRef.current, null, calcBarStyle, 0);
observerRef.current = observer;

return () => {
observerRef.current?.disconnect();
observerRef.current = null;
};
}, [radioGroupRef.current, internalValue]); // eslint-disable-line react-hooks/exhaustive-deps
}, []); // eslint-disable-line react-hooks/exhaustive-deps

const renderBlock = () => {
if (!variant.includes('filled')) {
Expand Down
Loading