在展示组件中嵌套容器组件
Nesting a container component in a presentational component
我正在尝试重构我的应用程序以分离展示组件和容器组件。我的容器组件只是包装在来自 react-redux 的 connect()
调用中的展示组件,它将状态和动作创建者映射到展示组件的道具。
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
组件需要的每个 state prop 和 action creator prop,并将它们全部传递到手动链接作为道具。当然,这是我想避免的事情,尤其是当我开始嵌套更多关卡或开始依赖更多来自 Redux 的道具时。
我的做法是否正确?似乎我一般不应该尝试将容器组件嵌入到展示组件中,因为如果我可以将展示组件与 Redux 分离,它们将变得更加可重用。同时,我不知道如何将需要访问 Redux state/dispatch 的组件嵌入到任何其他具有标记的组件中。
具体回答你的问题:可以嵌套展示组件和容器组件。毕竟,它们都只是组件。然而,为了便于测试,我更喜欢嵌套展示组件而不是容器组件。这一切都归结为组件的清晰结构。我发现从单个文件开始然后慢慢组件化效果很好。
查看嵌套子元素并利用 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>
);
}
}
待办事项(演示组件)
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>
);
}
};
我正在尝试重构我的应用程序以分离展示组件和容器组件。我的容器组件只是包装在来自 react-redux 的 connect()
调用中的展示组件,它将状态和动作创建者映射到展示组件的道具。
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
组件需要的每个 state prop 和 action creator prop,并将它们全部传递到手动链接作为道具。当然,这是我想避免的事情,尤其是当我开始嵌套更多关卡或开始依赖更多来自 Redux 的道具时。
我的做法是否正确?似乎我一般不应该尝试将容器组件嵌入到展示组件中,因为如果我可以将展示组件与 Redux 分离,它们将变得更加可重用。同时,我不知道如何将需要访问 Redux state/dispatch 的组件嵌入到任何其他具有标记的组件中。
具体回答你的问题:可以嵌套展示组件和容器组件。毕竟,它们都只是组件。然而,为了便于测试,我更喜欢嵌套展示组件而不是容器组件。这一切都归结为组件的清晰结构。我发现从单个文件开始然后慢慢组件化效果很好。
查看嵌套子元素并利用 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>
);
}
}
待办事项(演示组件)
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>
);
}
};