Ben*_*Ben 4 javascript immutable.js redux normalizr react-redux
目前正致力于react + redux项目.
我还使用normalizr来处理数据结构并重新选择以收集应用程序组件的正确数据.
一切似乎都运作良好.
我发现自己处于类似叶子的组件需要来自商店的数据的情况,因此我需要connect()组件来实现它.
作为一个简化示例,假设该应用程序是一个图书编辑系统,有多个用户收集反馈.
Book
Chapters
Chapter
Comments
Comments
Comments
Run Code Online (Sandbox Code Playgroud)
在应用程序的不同级别,用户可以贡献内容和/或提供评论.
考虑我正在渲染章节,它有内容(和作者)和评论(每个都有自己的内容和作者).
目前我会connect()和reselect章节内容基于ID.
因为数据库是用normalizr规范化的,所以我实际上只获得章节的基本内容字段和作者的用户ID.
为了呈现注释,我将使用一个可以重新选择链接到章节的注释的连接组件,然后单独呈现每个注释组件.
同样,因为数据库是用normalizr规范化的,所以我真的只得到评论作者的基本内容和用户ID.
现在,为了呈现像作者徽章一样简单的东西,我需要使用另一个连接的组件从我拥有的用户ID中获取用户详细信息(在呈现章节作者和每个评论作者时).
该组件将是这样简单的东西:
@connect(
createSelector(
(state) => state.entities.get('users'),
(state,props) => props.id,
(users,id) => ( { user:users.get(id)})
)
)
class User extends Component {
render() {
const { user } = this.props
if (!user)
return null
return <div className='user'>
<Avatar name={`${user.first_name} ${user.last_name}`} size={24} round={true} />
</div>
}
}
User.propTypes = {
id : PropTypes.string.isRequired
}
export default User
Run Code Online (Sandbox Code Playgroud)
它似乎工作正常.
我试图做相反的事情并将数据反规范化到更高的级别,以便例如章节数据将直接嵌入用户数据,而不仅仅是用户ID,并将其直接传递给用户 - 但仅限于似乎只是制作了非常复杂的选择器,并且因为我的数据是不可变的,所以它每次只重新创建对象.
所以,我的问题是,是否有类似叶子的组件(如上面的用户)connect()到商店呈现反模式的标志?
我做对了,还是看错了?
jay*_*soo 10
我认为你的直觉是正确的.在任何级别(包括叶节点)连接组件都没有错,只要API有意义 - 也就是说,给定一些道具可以推断组件的输出.
智能与哑组件的概念有点过时了.相反,最好考虑连接组件与未连接组件.在考虑是否创建连接组件与未连接组件时,需要考虑一些事项.
如果您将应用划分为较小的模块,通常最好将其交互限制在较小的API表面.例如,假设users并且comments在单独的模块中,那么我会说<Comment>组件使用连接<User id={comment.userId}/>组件而不是让它自己抓取用户数据更有意义.
具有太多责任的连接组件是代码气味.例如,<Comment>组件的职责可以是获取注释数据并进行呈现,并以操作调度的形式处理用户交互(与注释一起).如果它需要处理抓取用户数据,并处理与用户模块的交互,那么它做得太多了.最好将相关职责委托给另一个连接组件.
这也被称为"脂肪控制器"问题.
通过在顶部放置一个大的连接组件来传递数据,它实际上会对性能产生负面影响.这是因为每个状态更改都将更新顶级引用,然后每个组件将被重新呈现,并且React将需要对所有组件执行协调.
Redux通过假设它们是纯粹的来优化连接组件(即如果prop引用是相同的,则跳过重新呈现).如果连接叶节点,则状态更改将仅重新呈现受影响的叶节点 - 跳过大量协调.这可以在这里看到:https://github.com/mweststrate/redux-todomvc/blob/master/components/TodoItem.js
我要提到的最后一件事是重用和测试.如果你需要1)将它连接到state atom的另一部分,连接组件是不可重用的,2)直接传入数据(例如我已经有user数据,所以我只想要一个纯渲染).同样,连接的组件更难测试,因为您需要先设置它们的环境才能渲染它们(例如创建存储,传递存储<Provider>等).
通过在有意义的地方导出已连接和未连接的组件,可以减轻此问题.
export const Comment = ({ comment }) => (
<p>
<User id={comment.userId}/>
{ comment.text }
</p>
)
export default connect((state, props) => ({
comment: state.comments[props.id]
}))(Comment)
// later on...
import Comment, { Comment as Unconnected } from './comment'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
678 次 |
| 最近记录: |