我正在尝试创建一个自定义的挂钩,该挂钩将为我们处理一些阿芙罗狄蒂(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问题?
答案 0 :(得分: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
)
)
};
};