Как использовать пользовательские функциональные компоненты в меню Material-UI - PullRequest
1 голос
/ 25 мая 2019

Я использую Material-UI для разработки приложения, которое я создаю.Я пытаюсь использовать свой собственный функциональный компонент внутри компонента <Menu> и получаю странную ошибку:

Warning: Function components cannot be given refs.
Attempts to access this ref will fail.
Did you mean to use React.forwardRef()?

Check the render method of ForwardRef(Menu).
    in TextDivider (at Filter.js:32)

JSX, где это происходит, выглядит следующим образом: (Строка 32 - компонент TextDivider)

<Menu>
    <TextDivider text="Filter Categories" />
    <MenuItem>...</MenuItem>
</Menu>

Где TextDivider:

const TextDivider = ({ text }) => {
    const [width, setWidth] = useState(null);
    const style = {
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center'
    };

    const hrStyle = {
        width: `calc(100% - ${width}px)`,
        marginRight: 0,
        marginLeft: 0
    };

    const spanStyle = {
        color: '#555',
        whiteSpace: 'nowrap'
    };

    const ref = createRef();
    useEffect(() => {
        if (ref.current) {
            const width = ref.current.getBoundingClientRect().width + 35;
            console.log('width', width);
            setWidth(width);
        }
    }, [ref.current]);

    return (
        <div style={style}>
            <span ref={ref} style={spanStyle}>{ text }</span>
            <Divider className="divider-w-text" style={ hrStyle }/>
        </div>
    );
};

Странная часть заключается в том, что этот компонент прекрасно воспроизводится, когда устанавливается сам в обычном контейнере, но, похоже, выдает только эту ошибкупри рендеринге внутри компонента <Menu>.

Более того, мне показалось действительно странным, что эта ошибка полностью исчезает, если я просто обертываю TextDivider в div следующим образом:

<Menu>
    <div>
        <TextDivider text="Filter Categories" />
    </div>
    <MenuItem>...</MenuItem>
</Menu>

Однакодля меня это похоже на клейкую ленту, и я бы лучше понял основную проблему, почему я не могу просто визуализировать этот компонент в меню.Я не использую какие-либо ссылки Material-UI в своем функциональном компоненте, и я не помещаю ссылку на компонент <Divider> в моем компоненте <TextDivider>, поэтому я не понимаю, почему он выдает эту ошибку.

Буду очень признателен за понимание причин такого поведения.

Я пытался использовать useCallback hook, createRef(), useRef() и читать все, что смог найти при использованиикрючки внутри функционального компонента.Сама ошибка кажется вводящей в заблуждение, потому что даже сами реактивные документы показывают, используя ссылки на функциональные компоненты здесь: https://reactjs.org/docs/hooks-reference.html#useref

1 Ответ

1 голос
/ 26 мая 2019

Menu использует первый дочерний элемент меню в качестве «привязки контента» для компонента Popover, который используется внутри меню.«Содержание якорь» является элементом DOM, в меню, которое поповер пытается выстраиваться с якорем элементом (элемент за пределами меню, которое является точкой отсчета для позиционирования меню).

Для того, чтобы рычагиПервый дочерний элемент в качестве привязки контента, Menu добавляет ссылку на него (используя cloneElement).Чтобы не получить полученную ошибку (и чтобы позиционирование работало правильно), вашему функциональному компоненту необходимо переслать ссылку на один из компонентов, которые он отображает (обычно это самый внешний компонент - div в вашем случае).

Когда вы используете div в качестве прямого потомка Menu, вы не получите ошибку, потому что div может успешно получить ссылку.

Документация, на которую ссылается SakoBu, содержит подробности о том, какпересылать ссылки на функциональные компоненты.

Результат должен выглядеть примерно так (но я отвечаю на это на моем телефоне, извините, если есть какие-либо синтаксические ошибки):

const TextDivider = React.forwardRef(({ text }, ref) => {
    const [width, setWidth] = useState(null);
    const style = {
        display: 'flex',
        justifyContent: 'space-between',
        alignItems: 'center'
    };

    const hrStyle = {
        width: `calc(100% - ${width}px)`,
        marginRight: 0,
        marginLeft: 0
    };

    const spanStyle = {
        color: '#555',
        whiteSpace: 'nowrap'
    };
    // Separate bug here.
    // This should be useRef instead of createRef.
    // I’ve also renamed this ref for clarity, and used "ref" for the forwarded ref.
    const spanRef = React.useRef();
    useEffect(() => {
        if (spanRef.current) {
            const width = spanRef.current.getBoundingClientRect().width + 35;
            console.log('width', width);
            setWidth(width);
        }
    }, [spanRef.current]);

    return (
        <div ref={ref} style={style}>
            <span ref={spanRef} style={spanStyle}>{ text }</span>
            <Divider className="divider-w-text" style={ hrStyle }/>
        </div>
    );
});

Выможет также оказаться полезным следующий ответ:

В чем разница между `useRef` и` createRef`?

...