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

时间:2019-02-12 00:55:01

标签: javascript reactjs react-hooks 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样式表以用于该组件实例。

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

期望:根据是否传递了道具以覆盖该样式,在两条路线之间单击会更改文本的颜色。

实际:合并样式后,即使没有传递其他道具的路线也会以覆盖的颜色显示出来。

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

1 个答案:

答案 0 :(得分:1)

此处的关键是详细了解Array.reduce的行为。 reduce接受两个参数。第一个参数是您指定的回调。第二个参数是可选的,并且是累加器的初始值(回调的第一个参数)。这是该参数的说明:

  

用作回调的第一次调用的第一个参数的值。   如果未提供初始值,则数组中的第一个元素将   使用。在没有初始值的空数组上调用reduce()   是一个错误。

要更轻松地了解其效果,它将有助于简化语法。

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

Edit yqjjmmp21x