我应该如何将redux与不会被重用的嵌套子组件一起使用?

时间:2015-12-22 22:49:12

标签: javascript reactjs redux

我正在研究一个包含许多子组件的组件,其中一些子组件嵌套了五个子组件。我有兴趣使用redux来获得在共同状态原子中具有单一真实来源的优势。

我不理解的是智能/愚蠢的组件推荐,并将提供者置于主要组件之上,并通过道具传递所有内容。如果我这样做,那么我需要将道具一直传递到第五个嵌套项目,这样它就可以进行回调以调度一个动作或查看只有它需要的状态,而不是它的父项。我理解这是用于代码重用,但子组件永远不会在主组件之外使用。这里推荐的解决方案是什么?仍然使用道具?

注意:此库的作者要求我们在StackOverflow上提问。我之所以提到这一点,是因为SO似乎将“最佳实践”问题标记得太模糊了。

3 个答案:

答案 0 :(得分:9)

虽然发布的answer亚光克拉门斯确实涵盖了这一点,但我会在这里尝试更深入。

您可以在任何级别使用connect() 。这样做会使组件智能,因为它知道props的来源。 dumb 组件只有props,它们可以来自任何地方。智能组件耦合到redux;一个愚蠢的组成部分不是。

There are differing opinions on this approach,但支持且有效。

绘制此行的位置完全取决于您,但让我们看一个示例。您有一些聊天客户端,其标准侧边栏组件,消息窗口和用于发送新消息的输入字段。

+---------+--------------------------+
|         |                          |
|Sidebar  |  Messages window         |
|         |                          |
|         |                          |
|         |                          |
|         |                          |
|         +--------------------------+
|         |  New Message Entry     **|
|         |                          |
+---------+--------------------------+

所有这些的父级将使用connect()从redux获取数据并通过props将其提供给这些组件。现在想象除new message entry之外的那两个星号打开一个设置面板(忽略愚蠢的位置,这是一个例子)。 new message entry通过这些道具真的有意义吗?不,它没有。

要解决此问题,您可以创建一个特殊的"容器",让我们调用它SettingsContainer使用connect()来获取它的道具,它所做的只是将它们传递给{{ 1}}。 SettingsPopup不会知道redux,并且仍然可以正常测试/样式化/重用,而新的消息条目只需要知道SettingsPopup,而不是任何依赖。

这种方法可以很好地扩展,但它有两个惩罚。首先,智能包装"像SettingsContainer这样的组件必须由其他哑组件使用。这使新消息条目组件的测试变得复杂。其次,顶级组件不再公开数据依赖关系的整个图形,这使得在不深入研究组件层次结构的情况下更难以推理。

这些权衡可能是值得的,但你应该知道它们。

答案 1 :(得分:5)

您可以使用' react-redux'的组件context来使用Provider的新React功能。使用Provider将抽象出一些上下文的实现细节,使你的标记非常富有表现力。

您基本上设置了一个迷你全局属性,所有子组件,哑或智能可以引用:

import React from 'react';
import {render} from 'react-dom';
import {createStore} from 'redux';
import {Provider} from 'react-redux'; //Special Redux component to help
import {reducers} from './reducers.js';


var DeepDumbChild = (props, context) => (
    <div>
        <pre>
            {JSON.stringify(data, null, 2)}
        </pre>
    </div>
)


class SmartChild extends React.Component {
    render() {
        /* Use the global-like context */
        let data = this.context.store.getState();
        return (
            <div>
                <DeepDumbChild data={data}/>
            </div>
        )
    }
}
SmartChild.contextTypes = {
    store: React.PropsTypes.object /* required */
}

/* No messy props :) */
var App = () => (<div>
    <SmartChild/>
</div>);


render(
    <Provider store={createStore(reducers)}>
        <App/>
    </Provider>,
    document.getElementById('app')
);

答案 2 :(得分:3)

更新:如果您想尝试以下方法,请查看我最近发布的https://github.com/artsy/react-redux-controller

我认为最好的方法是将选择器映射到根(容器)组件的data= cube( array[cube_ll_coord(a.data, 1), cube_ll_coord(a.data, 2), cube_ll_coord(a.data, 3), cube_ll_coord(a.data, 4)], array[cube_ur_coord(a.data, 1), cube_ur_coord(a.data, 1), cube_ur_coord(a.data, 3), cube_ur_coord(b.data, 4)] ) ,而不是拥有一堆容器或在任何地方使用context。我在自己的项目中做到了这一点并且工作得非常好。它添加了一个新的模式,用它们产生的connect注释选择器。这让我使用PropType来允许所有后代保留与childContextTypes相同的保护。这比传递传递下来的大型无类型对象Ashley Coolman's approach更有优势。我希望在接下来的几天里有时间将其纳入自己的图书馆并发布。

与此同时,我建议making components smart使用propTypes自由,而不是Tyrsius's approach创建一大堆容器,但那只是我的意见。