Передача импортированного объекта стиля в пользовательский хук не работает должным образом

Я пытаюсь создать пользовательский хук, который будет обрабатывать некоторые стили Aphrodite ( https://github.com/Khan/aphrodite) для нас, но я получаю необычное поведение при передаче объекта стилей.

Вот мой хук: (план состоял в том, чтобы сочинять с useMemo, поэтому это вообще хук)

import castArray from 'lodash/castArray';
import {StyleSheet} from 'aphrodite';
import merge from 'lodash/merge';

export const useStyles = (styles, props) => {
    return {
        styles: StyleSheet.create(
            [...castArray(styles), ...castArray(props.styles)]
                .reduce((agg, next) => (merge(agg, next))),
        )
    };
};

И основное использование:

function TestComponent(props) {
    const {styles} = useStyles(baseStyles, props);

    return <div className={css(styles.test)}>Test Text</div>
}

С мыслью, что если styles передается prop, он будет объединен с чем угодно в baseStyles и даст окончательную таблицу стилей Aphrodite, которая будет использоваться для этого экземпляра компонента.

Я создал простой репозиторий здесь: https://github.com/bslinger/hooks-question

Ожидание: щелчок между двумя маршрутами изменит цвет текста в зависимости от того, были ли переданы реквизиты для переопределения этого стиля или нет.

Фактически: после объединения стилей даже маршрут без пропуска дополнительных реквизитов показывает его переопределенным цветом.

Примечание: я понял, что после удаления useMemo это даже не было технически хуком, и понижение React до 16.7 привело к тому же самому поведению, так что я думаю, что это всего лишь вопрос Javascript или React?

1 ответ

Решение

Ключевым моментом здесь является детальное понимание поведения Array.reduce. reduce принимает два аргумента. Первый аргумент - это обратный вызов, который вы указали. Второй аргумент является необязательным и является начальным значением для аккумулятора (первый аргумент обратного вызова). Вот описание этого аргумента:

Значение, используемое в качестве первого аргумента для первого вызова обратного вызова. Если начальное значение не указано, будет использован первый элемент в массиве. Вызов метода Reduce() для пустого массива без начального значения является ошибкой.

Чтобы понять эффект этого легче, это поможет упростить ваш синтаксис.

До тех пор, пока styles а также props.styles не являются массивами (которых нет в вашем образце), следующие:

[...castArray(styles), ...castArray(props.styles)]

эквивалентно:

[styles, props.styles]

Таким образом, при отсутствии начального значения reduce функция, аккумулятор будет первым элементом в вашем массиве: styles, Поэтому, выполнив сценарий "withProps", вы изменили объект в styles.js и ничто не изменит его обратно к исходному зеленому. Если styles был массив (с использованием исходного кода), то этот побочный эффект будет происходить с первым объектом стиля в этом массиве.

Чтобы это исправить, вам просто нужно указать начальное значение для аккумулятора:

export const useStyles = (styles, props) => {
  return {
    styles: StyleSheet.create(
      [...castArray(styles), ...castArray(props.styles)].reduce(
        (agg, next) => merge(agg, next),
        {} // Here's an empty object as the accumulator initial value
      )
    )
  };
};

Редактировать yqjjmmp21x

Другие вопросы по тегам