我正在尝试重构我的应用程序以分离表示和容器组件。我的容器组件只是包装在connect()react- redux调用中的表示性组件,这些调用将状态和动作创建者映射到表示性组件的props。
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组件将需要并将它们作为道具手动传递给整个链。当然,这是我要避免的事情,尤其是当我开始嵌套更多的关卡或开始依赖Redux的更多道具时。
<TodoContainer />
TodoList
<Todo />
TodoListContainer
Todo
我正确地解决了吗?似乎我一般不应该尝试将容器组件嵌入表示性组件内,因为如果我可以将表示性组件与Redux分离,它们将变得更可重用。同时,我不知道如何在需要标记的任何其他组件内部嵌入需要访问Redux状态/调度的组件。
具体回答您的问题:可以嵌套演示和容器组件。毕竟,它们都是组件。但是为了方便测试,我宁愿嵌套表示组件而不是容器组件。一切都归结为组件的清晰结构。我发现从一个文件开始,然后慢慢进行组件大小调整就可以了。
看一下嵌套子级并利用this.props.children它们将子级元素包装在表示性组件中。
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> ); } }
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);
import React, { Component } from 'react'; import TodoList from 'containers/TodoList'; export default class DashboardView extends Component { render () { return ( <div> <TodoList /> </div> ); } };