Передача импортированного объекта стиля в пользовательский хук не работает должным образом
Я пытаюсь создать пользовательский хук, который будет обрабатывать некоторые стили 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
)
)
};
};