在展示组件中嵌套容器组件 [英] Nesting a container component in a presentational component

查看:41
本文介绍了在展示组件中嵌套容器组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试重构我的应用程序以分离展示组件和容器组件.我的容器组件只是封装在来自 react-redux 的 connect() 调用中的展示组件,它将状态和动作创建者映射到展示组件的 props.

todo-list.container.js

import React, {Component} from 'react';从'react-redux'导入{connect};从 '../actions/todo.actions' 导入 {fetchTodos};从 '../components/todo-list.component' 导入 TodoList;export default connect(({todo}) => ({state: {todo}}), {fetchTodos})(TodoList);

todo-list.component.jsx

import React, {Component} from 'react';从 '../containers/todo.container' 导入 TodoContainer;导出默认类 TodoList 扩展组件 {组件DidMount(){this.props.fetchTodos();}使成为 () {const todoState = this.props.state.todo;返回 (<ul className="list-unstyled todo-list">{todoState.order.map(id => {const todo = todoState.todos[id];return <li key={todo.id}><TodoContainer todo={todo}/></li>;})});}};

todo.container.js

import React, {Component} from 'react';从'react-redux'导入{connect};从 '../actions/todo.actions' 导入 {createTodo, updateTodo, deleteTodo};从 '../components/todo.component' 导入 Todo;export default connect(null, {createTodo, updateTodo, deleteTodo})(Todo);

todo.component.jsx

import React, {Component} from 'react';导入'../styles/todo.component.css';导出默认类 Todo 扩展组件 {使成为 () {返回 (<div className="todo">{todo.description}

);}};

我想弄清楚的是:我知道我应该<TodoContainer/> 元素嵌入 TodoList 因为 TodoList 是一个展示组件,它应该只在其中嵌套其他展示组件.但是如果我只用一个 <Todo/> 表示组件替换它,那么我必须在 TodoListContainer 中映射每个状态道具和动作创建者道具,Todo 组件需要将它们作为道具手动传递到链中.这当然是我想要避免的,尤其是当我开始嵌套更多关卡或开始依赖来自 Redux 的更多道具时.

我是否正确地处理了这个问题?似乎我一般不应该尝试将容器组件嵌入到展示组件中,因为如果我可以将展示组件从 Redux 中分离出来,它们就会变得更加可重用.同时,我不知道如何将需要访问 Redux state/dispatch 的组件嵌入到任何其他具有标记的组件中.

解决方案

专门回答您的问题:嵌套展示组件和容器组件是可以的.毕竟,它们都只是组件.然而,为了便于测试,我更喜欢嵌套表示组件而不是容器组件.这一切都归结为组件的清晰结构.我发现从单个文件开始,然后慢慢地组件化效果很好.

看看嵌套子元素并利用 this.props.children 将子元素包装在展示组件中.

示例(为简洁起见删除了一些代码)

列表(展示组件)

import React, { Component, PropTypes } from 'react';导出默认类列表扩展组件{静态 propTypes = {孩子:PropTypes.node}使成为 () {返回 (<div className="generic-list-markup">{this.props.children} <----- 包装所有孩子

);}}

Todo(展示组件)

import React, { Component, PropTypes } from 'react';导出默认类 Todo 扩展组件 {静态 propTypes = {描述:PropTypes.string.isRequired}使成为 () {返回 (<div className="generic-list-markup">{this.props.description}

);}}

TodoList(容器组件)

import React, { Component, PropTypes } from 'react';从'react-redux'导入{连接};import { createTodo, updateTodo, deleteTodo } from 'actions';从组件/列表"导入列表;从组件/待办事项"导入待办事项;导出类 TodoList 扩展组件 {静态 propTypes = {待办事项:PropTypes.array.isRequired,创建:PropTypes.func.isRequired}使成为 () {返回 (<div><列表><----------- 使用我们的展示组件{this.props.todos.map((todo, key) =><Todo key={key} description={todo.description}/>)}</列表><a href="#" onClick={this.props.create}>添加待办事项</a>

);}}const stateToProps = state =>({待办事项:state.todos});const dispatchToProps = dispatch = ({创建:() =>调度(createTodo())});导出默认连接(stateToProps,dispatchToProps)(TodoList);

DashboardView(展示组件)

import React, { Component } from 'react';从 'containers/TodoList' 导入 TodoList;导出默认类 DashboardView 扩展组件 {使成为 () {返回 (<div><待办事项列表/>

);}};

I'm trying to refactor my app to separate presentational and container components. My container components are just the presentational components wrapped in connect() calls from react-redux, which map state and action creators to the presentational components' props.

todo-list.container.js

import React, {Component} from 'react';
import {connect} from 'react-redux';

import {fetchTodos} from '../actions/todo.actions';
import TodoList from '../components/todo-list.component';

export default connect(({todo}) => ({state: {todo}}), {fetchTodos})(TodoList);

todo-list.component.jsx

import React, {Component} from 'react';

import TodoContainer from '../containers/todo.container';

export default class TodoList extends Component {
    componentDidMount () {
        this.props.fetchTodos();
    }

    render () {
        const todoState = this.props.state.todo;

        return (
            <ul className="list-unstyled todo-list">
                {todoState.order.map(id => {
                    const todo = todoState.todos[id];
                    return <li key={todo.id}><TodoContainer todo={todo} /></li>;
                })}
            </ul>
        );
    }
};

todo.container.js

import React, {Component} from 'react';
import {connect} from 'react-redux';

import {createTodo, updateTodo, deleteTodo} from '../actions/todo.actions';
import Todo from '../components/todo.component';

export default connect(null, {createTodo, updateTodo, deleteTodo})(Todo);

todo.component.jsx

import React, {Component} from 'react';

import '../styles/todo.component.css';

export default class Todo extends Component {
    render () {
        return (
            <div className="todo">
                {todo.description}
            </div>
        );
    }
};

What I'm trying to figure out is this: I know I should not be embedding the <TodoContainer /> element inside of TodoList because TodoList is a presentational component and it should only nest other presentational components inside of it. But if I replace it with just a <Todo /> presentational component, then I have to map every state prop and action creator prop in TodoListContainer that the Todo component would need and pass them all down the chain manually as props. This is something I want to avoid of course, especially if I start nesting more levels or start depending on more props coming from Redux.

Am I approaching this correctly? It seems that I shouldn't be trying to embed a container component inside of a presentational component in general, because if I can decouple presentational components from Redux, they become more reusable. At the same time, I don't know how else to embed a component that requires access to Redux state/dispatch inside of any other component that has markup.

解决方案

To specifically answer your question: It is okay to nest presentational and container components. After all, they are all just components. In the interest of easy testing however, I would prefer nesting presentational components over container components. It all comes down to a clear structuring of your components. I find that starting in a single file and then slowly component-izing works well.

Have a look at nesting children and utilizing this.props.children to wrap child elements in a presentational component.

Example (removed some code for brevity)

List (presentational component)

import React, { Component, PropTypes } from 'react';

export default class List extends Component {
  static propTypes = {
    children: PropTypes.node
  }

  render () {
    return (
      <div className="generic-list-markup">
        {this.props.children} <----- wrapping all children
      </div>
    );
  }
}

Todo (presentational component)

import React, { Component, PropTypes } from 'react';

export default class Todo extends Component {
  static propTypes = {
    description: PropTypes.string.isRequired
  }

  render () {
    return (
      <div className="generic-list-markup">
        {this.props.description}
      </div>
    );
  }
}

TodoList (container component)

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { createTodo, updateTodo, deleteTodo } from 'actions';
import List from 'components/List';
import Todo from 'components/Todo';

export class TodoList extends Component {
  static propTypes = {
    todos: PropTypes.array.isRequired,
    create: PropTypes.func.isRequired
  }

  render () {
    return (
      <div>
        <List> <---------- using our presentational component
          {this.props.todos.map((todo, key) =>
            <Todo key={key} description={todo.description} />)}
        </List>
        <a href="#" onClick={this.props.create}>Add Todo</a>
      </div>
    );
  }
}

const stateToProps = state => ({
  todos: state.todos
});

const dispatchToProps = dispatch = ({
  create: () => dispatch(createTodo())
});

export default connect(stateToProps, dispatchToProps)(TodoList);

DashboardView (presentational component)

import React, { Component } from 'react';
import TodoList from 'containers/TodoList';

export default class DashboardView extends Component {
  render () {
    return (
      <div>
        <TodoList />
      </div>
    );
  }
};

这篇关于在展示组件中嵌套容器组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆