将导入的样式对象传递到自定义挂钩中无法按预期工作

Passing imported style object into a custom hook does not work as expected

我正在尝试创建一个自定义挂钩,它将为我们处理 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 道具,它将与 baseStyles 中的任何内容合并,并给出最终的 Aphrodite 样式表以用于该组件实例。

我在这里创建了一个简单的重现存储库:https://github.com/bslinger/hooks-question

预期:在两条路线之间点击会改变文本的颜色,这取决于是否传入了 props 以覆盖该样式。

实际:样式合并后,即使没有传入额外道具的路由也会显示覆盖的颜色。

注意:我意识到在删除 useMemo 之后,这在技术上什至不是一个钩子,将 React 降级到 16.7 会导致相同的行为,所以我想这毕竟只是一个 Javascript 或 React 问题?

这里的关键是详细了解 Array.reduce 的行为。 reduce 接受两个参数。第一个参数是您指定的回调。第二个参数是可选的,是累加器的初始值(回调的第一个参数)。以下是该参数的描述:

Value to use as the first argument to the first call of the callback. If no initial value is supplied, the first element in the array will be used. Calling reduce() on an empty array without an initial value is an error.

为了更容易理解它的效果,这将有助于简化您的语法。

只要 stylesprops.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
      )
    )
  };
};