web-dev-qa-db-fra.com

Imbrication d'un composant de conteneur dans un composant de présentation

J'essaie de refactoriser mon application pour séparer les composants de présentation et de conteneur. Mes composants de conteneur ne sont que les composants de présentation enveloppés dans des appels connect() de react-redux, qui mappent les créateurs d'état et d'action aux accessoires des composants de présentation.

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

Ce que j'essaie de comprendre, c'est ceci: je sais que je ne devrais pas pas intégrer le <TodoContainer /> élément à l'intérieur de TodoList car TodoList est un composant de présentation et il ne doit imbriquer que d'autres composants de présentation à l'intérieur. Mais si je le remplace par juste un <Todo /> composant de présentation, alors je dois mapper chaque accessoire d'état et accessoire créateur d'action dans TodoListContainer dont le composant Todo aurait besoin et les transmettre tous manuellement dans la chaîne comme accessoires. C'est quelque chose que je veux éviter bien sûr, surtout si je commence à imbriquer plus de niveaux ou à dépendre de plus d'accessoires provenant de Redux.

Suis-je en train de l'aborder correctement? Il semble que je ne devrais pas essayer d'incorporer un composant conteneur à l'intérieur d'un composant de présentation en général, car si je peux découpler les composants de présentation de Redux, ils deviennent plus réutilisables. Dans le même temps, je ne sais pas comment intégrer un composant qui nécessite un accès à l'état/répartition Redux à l'intérieur de tout autre composant ayant un balisage.

24
M Miller

Pour répondre spécifiquement à votre question: il est possible d'imbriquer des composants de présentation et de conteneur. Après tout, ce ne sont que des composants. Cependant, dans l'intérêt de tests faciles, je préférerais imbriquer des composants de présentation plutôt que des composants de conteneur. Tout se résume à une structuration claire de vos composants. Je trouve que le démarrage dans un seul fichier, puis le dimensionnement lent des composants fonctionnent bien.

Jetez un œil à l'imbrication des enfants et à l'utilisation de this.props.children pour envelopper les éléments enfants dans un composant de présentation.

Exemple (supprimé du code pour plus de concision)

Liste (composante de présentation)

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 (composante de présentation)

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 (composant conteneur)

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 (composant de présentation)

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

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