Вложение компонента контейнера в презентационный компонент
Я пытаюсь реорганизовать свое приложение, чтобы разделить компоненты представления и контейнера. Мои контейнерные компоненты - это просто презентационные компоненты, завернутые в connect()
вызовы из response-redux, которые отображают создателей состояний и действий на реквизиты компонентов представления.
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>
);
}
};
То, что я пытаюсь выяснить, это: я знаю, что я не должен встраивать <TodoContainer />
элемент внутри TodoList
так как TodoList
является презентационным компонентом, и в него должны быть вложены только другие презентационные компоненты. Но если я заменю его просто <Todo />
компонент представления, то я должен отобразить каждую пропу штата и действие пропеллер создателя в TodoListContainer
что Todo
Компонент будет необходимо и передать их все вниз по цепочке вручную, как реквизит Это то, чего я, конечно, хочу избежать, особенно если я начну вкладывать больше уровней или начну зависеть от большего количества реквизитов, поступающих из Redux.
Я правильно подхожу к этому? Кажется, что мне не следует пытаться встроить контейнерный компонент в презентационный компонент в целом, потому что, если я смогу отделить презентационные компоненты от Redux, они станут более пригодными для повторного использования. В то же время я не знаю, как еще встроить компонент, требующий доступа к состоянию / диспетчеризации Redux, внутри любого другого компонента, имеющего разметку.
1 ответ
Чтобы конкретно ответить на ваш вопрос: это нормально, чтобы вкладывать презентационные и контейнерные компоненты. В конце концов, они всего лишь компоненты. Однако в интересах легкого тестирования я бы предпочел вложение вложенных компонентов в компоненты контейнера. Все сводится к четкому структурированию ваших компонентов. Я считаю, что начиная с одного файла, а затем медленно компонент-izing работает хорошо.
Посмотрите на гнездование детей и использование this.props.children
обернуть дочерние элементы в презентационный компонент.
Пример (убран код для краткости)
Список (презентационный компонент)
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 (презентационный компонент)
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 (контейнерный компонент)
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 (презентационный компонент)
import React, { Component } from 'react';
import TodoList from 'containers/TodoList';
export default class DashboardView extends Component {
render () {
return (
<div>
<TodoList />
</div>
);
}
};