在同一页面/路由上有多个相同的可重用 redux react 组件实例

IT技术 javascript reactjs redux
2021-02-27 17:19:35

我们正在创建一个大型前端应用程序。

我们正在使用 React-Redux

我们正在创建一些可重用的组件。

这个问题是关于在同一页面/路由上有多个相同可重用 redux react 组件的实例

问题详情:

我们有一个Sectionheader组件。这绑定到 redux 状态。

它侦听标题属性 reducer SectionheaderReducer

由于我们Sectionheader在页面上有 2 个这样的实例,它们都倾向于显示相同的值,因为它们绑定到相同的商店状态属性。

如何使基于 redux 的可重用 React 组件可配置?以便每个实例可以具有不同的减速器标头属性值SectionheaderReducer

4个回答

您需要实现某种命名空间实例的方法。这可以像传递一个键来区分组件和减速器一样基本。

您可以ownPropsmapStateToProps函数中使用 来引导映射到命名空间

const mapStateToProps = (state, ownProps) {
    let myState = state[ownProps.namespace]
    return {
        myState.value
    }
}

可以使用相同的方法将命名空间传递给 mapDispatchToProps

const mapDispatchToProps = (dispatch, ownProps) {
    return {
        myAction: (myParam) => dispatch(myAction(ownProps.namespace, myParam))
    }
}

只要记住在动作类型中使用命名空间,这样减速器就不会踩到脚趾

const myAction => (namespace, myParam) {
    return { type: `${namespace}/${MY_TYPE_CONSTANT}`, myParam }
}

并确保减速器也有命名空间

const myReducer = (namespace) => (state = initialState, action) => {
    switch(action.type) {
        case `${namespace}/${MY_TYPE_CONSTANT}`:
            return { ...state, action.myParam }
        default:
            return state
    {
}

现在在组合减速器时添加 2 个命名空间减速器

combineReducers({
    myInstance1 : myReducer('myInstance1')
    myInstance2 : myReducer('myInstance2')
}

最后将命名空间传递给每个实例

render() {
    return (
        <div>
            <MyComponent namespace='myInstance1' />
            <MyComponent namespace='myInstance2' />
        </div>
    )
}

免责声明:我是以下库的主要贡献者。

redux-subspace可以提供更高级的命名空间实现,而无需为每个想要拥有多个实例的组件重新实现此模式。

创建减速器与上面类似

const reducer = combineReducers({ 
    myInstance1: namespaced('myInstance1')(myReducer)
    myInstance2: namespaced('myInstance2')(myReducer)
})

然后SubspaceProvider可以用来切换每个组件的状态

render() {
    return (
        <div>
            <SubspaceProvider mapState={state => state.myInstance1} namespace='myInstance1'>
                <MyComponent />
            </SubspaceProvider>
            <SubspaceProvider mapState={state => state.myInstance2} namespace='myInstance2'>
                <MyComponent />
            </SubspaceProvider>
        </div>
    )
}

只需确保您还将mapStateToProps函数更改为从提供程序中映射的子树开始遍历

const mapStateToProps = (state) {
    return {
        state.value
    }
}

如果您希望减少嵌套,还有一个高阶组件

这个答案......就是为什么我永远不会使用 Redux 或任何这些单例模式。需要做这种事情来支持某事的两个实例,这完全违背了软件开发的概念。这通常在 Vue、React State、Angular (2/4+)、Polymer 和 web 组件中解决。然而,React 社区无缘无故地继续推动 Redux。
2021-04-22 17:19:35
对于现在遇到这个问题的任何人,作为对@coding 评论的回应:这是正确的,redux singelton 模式不是很可重用,但这不是它要解决的问题。当您希望树中任何位置的组件修改/使用相同的数据而不传递大量的 props 和回调时,Redux 非常有用(即,正是在您想要单例/全局时)。当您需要渲染所有行为相同的组件的 N 个副本时,内部组件状态是一种更好的方法。当您需要例如 3 个相同减速器的副本时,此答案是一个快乐的媒介。
2021-04-25 17:19:35
我同意在 mapStateToProps 中使用 props 是必要的,但是所有的逻辑都可以在 OP 编写的组件内完成,这样 OP 的组件只需要一个作为 prop 传递的唯一键,而不会强迫组件的用户必须组合多个减速器。在内部,OP 的组件将使用 mapStateToProps 将键包含在其调度操作中,但组件的化简器可以负责在其自己的命名空间内分配有效的子命名空间,并且(工厂)选择器负责确保每个组件使用正确的子命名空间.
2021-05-15 17:19:35
@MichaelPeyper 如果您有 N 个实例,其中 N 可以并且将通过 API 调用更改和确定,该怎么办?在这种情况下,我不确定我将拥有多少个组件实例。任何想法如何处理这种情况?目前,我正在为那个工作正常的组件使用本地状态。
2021-05-18 17:19:35
嗨迈克尔 - 我面临同样的问题,我在页面上有多个使用相同操作和减速器的组件实例。我尝试了您的解决方案,但无法使其正常工作。您是否有机会快速浏览一下并更新我的问题的代码? stackoverflow.com/questions/44640679/...
2021-05-19 17:19:35

我以不同的方式实现了它,实际上没有使用命名空间更改操作名称。

相反,我添加了 infra 函数,它将拦截动作创建者并添加meta-data到每个动作中。(以下FSA)这样你就不需要改变你的减速器或mapStateToProps功能。

它也与redux-thunk.

应该很容易使用... reducer-action-interceptor

我将这个问题解释为:

  • 您在商店中有内容数据(例如部分及其标题)
  • 您有用于绘制数据位的组件(例如您的<SectionHeader />
  • 您想在页面上显示多个部分,但目前所有标题都具有相同的文本

一种可能的解决方案是让您将“部分”的想法添加到您的商店。您将创建管理数据内容结构的 reducer。EG store 状态,可能看起来像这样:

{ 
  sections: {
     0: {
        header: 'My section title',
        content: 'Whatever your content is'
     },
     1: {
        header: 'My other section title',
        content: 'Loads of lovely writing or hrefs to images or whatever'
     }
  }
}

``

然后,您将拥有一个“容器组件”或“布局组件”或“智能组件”(它们有很多名称),它们“知道”您要在特定页面上使用第 2 部分和第 4 部分。它如何知道这一点,取决于您。也许你对索引进行了硬编码(因为它总是相同的),也许你有一个过滤规则,也许你在商店中有另一个定义选择的字段......等等。

然后容器组件会将选择的标题传递给 "dumb" ,可能是这样的:

<SectionHeader>{sections[2].header}</SectionHeader>

或者

<SectionHeader title={sections[2].header} />

将我们的组件转换为哑(无状态)组件,以便这些组件可以轻松重用而不会出现任何复杂情况。