仍然是新的反应和还原,并且一直在开发我现在正在使用的MERN用户注册应用程序。
在redux文档中,我发现创建者建议在将redux与react集成时建议将其代码分成两种类型的组件:Presentational(关注事物外观)和Container(关注事物如何运作)。参见https://redux.js.org/basics/usagewithreact。
我认为这样可以更好地管理应用程序和扩展性。
对于不熟悉的人,这里是优点的很好解释:https://www.youtube.com/watch?v=NazjKgJp7sQ
我只是在努力理解概念并以这种方式重写代码而苦苦挣扎。
这里是我用来显示用户创建的评论的帖子组件的示例。它从作为道具传递的更高级别组件中的帖子中接收数据。作为回报,我将所有带有自举样式的标记都应用了。我正在通过创建事件处理程序来订阅导入并使用的redux操作。
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import classnames from 'classnames';
import { Link } from 'react-router-dom';
import { deletePost, addLike, removeLike } from '../../actions/postActions';
class PostItem extends Component {
onDeleteClick(id) {
this.props.deletePost(id);
}
onLikeClick(id) {
this.props.addLike(id);
}
onUnlikeClick(id) {
this.props.removeLike(id);
}
findUserLike(likes) {
const { auth } = this.props;
if (likes.filter(like => like.user === auth.user.id).length > 0) {
return true;
} else {
return false;
}
}
render() {
const { post, auth, showActions } = this.props;
return (
<div className="card card-body mb-3">
<div className="row">
<div className="col-md-2">
<a href="profile.html">
<img
className="rounded-circle d-none d-md-block"
src={post.avatar}
alt=""
/>
</a>
<br />
<p className="text-center">{post.name}</p>
</div>
<div className="col-md-10">
<p className="lead">{post.text}</p>
{showActions ? (
<span>
<button
onClick={this.onLikeClick.bind(this, post._id)}
type="button"
className="btn btn-light mr-1"
>
<i
className={classnames('fas fa-thumbs-up', {
'text-info': this.findUserLike(post.likes)
})}
/>
<span className="badge badge-light">{post.likes.length}</span>
</button>
<button
onClick={this.onUnlikeClick.bind(this, post._id)}
type="button"
className="btn btn-light mr-1"
>
<i className="text-secondary fas fa-thumbs-down" />
</button>
<Link to={`/post/${post._id}`} className="btn btn-info mr-1">
Comments
</Link>
{post.user === auth.user.id ? (
<button
onClick={this.onDeleteClick.bind(this, post._id)}
type="button"
className="btn btn-danger mr-1"
>
<i className="fas fa-times" />
</button>
) : null}
</span>
) : null}
</div>
</div>
</div>
);
}
}
PostItem.defaultProps = {
showActions: true,
};
PostItem.propTypes = {
deletePost: PropTypes.func.isRequired,
addLike: PropTypes.func.isRequired,
removeLike: PropTypes.func.isRequired,
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
};
const mapStateToProps = state => ({
auth: state.auth,
});
export default connect(mapStateToProps, { deletePost, addLike, removeLike })(PostItem);
如您所见,代码没有我想要的那么整洁和紧凑。我的目标是使呈现组件不知道redux,并在此处进行所有样式和引导工作,而容器组件具有redux和connect功能。有人知道我该怎么做吗?
我看到人们使用connect将这些类型的组件链接在一起:
const PostItemContainer = connect(
mapStateToProps,
{ deletePost, addLike, removeLike }
)(PostItem);
export default PostItemContainer;
但是我不知道如何在实践中实现这一目标。 如果您能帮助我解释并提供一些示例代码,那就太好了。
谢谢!
答案 0 :(得分:1)
我总是将我的html(presentation)代码放在另一个文件中,作为响应,他们将它们称为无状态组件,
关键组件是PostItemComponent,它对Redux一无所知。
请参见以下代码:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import classnames from 'classnames';
import { Link } from 'react-router-dom';
import { deletePost, addLike, removeLike } from '../../actions/postActions';
const PostItemComponent = ({
post,
showActions,
auth,
onLikeClick,
findUserLike,
onUnlikeClick,
onDeleteClick
}) => {
return (
<div className="card card-body mb-3">
<div className="row">
<div className="col-md-2">
<a href="profile.html">
<img
className="rounded-circle d-none d-md-block"
src={post.avatar}
alt=""
/>
</a>
<br />
<p className="text-center">{post.name}</p>
</div>
<div className="col-md-10">
<p className="lead">{post.text}</p>
{showActions ? (
<span>
<button
onClick={(event) => onLikeClick(event, post._id)}
type="button"
className="btn btn-light mr-1">
<i
className={classnames('fas fa-thumbs-up', {
'text-info': findUserLike(post.likes)
})}
/>
<span className="badge badge-light">{post.likes.length}</span>
</button>
<button
onClick={(event) => onUnlikeClick(event, post._id)}
type="button"
className="btn btn-light mr-1"
>
<i className="text-secondary fas fa-thumbs-down" />
</button>
<Link to={`/post/${post._id}`} className="btn btn-info mr-1">
Comments
</Link>
{post.user === auth.user.id ? (
<button
onClick={(event) => onDeleteClick(event, post._id)}
type="button"
className="btn btn-danger mr-1"
>
<i className="fas fa-times" />
</button>
) : null}
</span>
) : null}
</div>
</div>
</div>
);
};
class PostItem extends Component {
constructor(props) {
super(props);
this.onDeleteClick = this.onDeleteClick.bind(this);
this.onLikeClick = this.onLikeClick.bind(this);
this.onUnlikeClick = this.onUnlikeClick.bind(this);
this.findUserLike = this.findUserLike.bind(this);
}
onDeleteClick(event, id) {
event.preventDefault();
this.props.deletePost(id);
}
onLikeClick(event, id) {
event.preventDefault();
this.props.addLike(id);
}
onUnlikeClick(event, id) {
event.preventDefault();
this.props.removeLike(id);
}
findUserLike(likes) {
const { auth } = this.props;
if (likes.filter(like => like.user === auth.user.id).length > 0) {
return true;
} else {
return false;
}
}
render() {
const { post, auth, showActions } = this.props;
return (
<PostItemComponent
post={post}
auth={auth}
showActions={showActions}
onDeleteClick={this.onDeleteClick}
onLikeClick={this.onLikeClick}
onUnlikeClick={this.onUnlikeClick}
/>
);
}
}
PostItem.defaultProps = {
showActions: true,
};
PostItem.propTypes = {
deletePost: PropTypes.func.isRequired,
addLike: PropTypes.func.isRequired,
removeLike: PropTypes.func.isRequired,
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
};
const mapStateToProps = state => ({
auth: state.auth,
});
export default connect(mapStateToProps, { deletePost, addLike, removeLike })(PostItem);
答案 1 :(得分:0)
这与@jsDevia的答案非常相似,但由于您说您的Post
组件已经连接到Redux,因此在这里我没有创建单独的组件。因此,您可以抓住所有动作创建者并在其中声明,然后将其传递给您的PostItem
组件。
第二个区别是我使用功能组件而不是类组件,因为这里不需要任何状态或生命周期方法。
第三个差异很小。我从您的onClick
处理程序中删除了所有绑定。对于this
范围问题,我将箭头函数用于处理程序。同样,我们不需要任何参数,例如post._id
来传递这些函数,因为这里已经有post
作为道具。这是分离组件的美妙之处。
在回调处理程序中使用bind
或箭头函数会导致大型应用程序出现性能问题,这些大型应用程序包含许多组件,例如Post
。由于这些函数每次都将重新创建,因此会重新创建。但是,使用函数引用可以防止这种情况。
const PostItem = ({
post,
deletePost,
addLike,
removeLike,
auth,
showActions,
}) => {
const onDeleteClick = () => deletePost(post._id);
const onLikeClick = () => addLike(post._id);
const onUnlikeClick = () => removeLike(post._id);
const findUserLike = likes => {
if (likes.filter(like => like.user === auth.user.id).length > 0) {
return true;
} else {
return false;
}
};
return (
<div className="card card-body mb-3">
<div className="row">
<div className="col-md-2">
<a href="profile.html">
<img
className="rounded-circle d-none d-md-block"
src={post.avatar}
alt=""
/>
</a>
<br />
<p className="text-center">{post.name}</p>
</div>
<div className="col-md-10">
<p className="lead">{post.text}</p>
{showActions ? (
<span>
<button
onClick={onLikeClick}
type="button"
className="btn btn-light mr-1"
>
<i
className={classnames("fas fa-thumbs-up", {
"text-info": findUserLike(post.likes),
})}
/>
<span className="badge badge-light">{post.likes.length}</span>
</button>
<button
onClick={onUnlikeClick}
type="button"
className="btn btn-light mr-1"
>
<i className="text-secondary fas fa-thumbs-down" />
</button>
<Link to={`/post/${post._id}`} className="btn btn-info mr-1">
Comments
</Link>
{post.user === auth.user.id ? (
<button
onClick={onDeleteClick}
type="button"
className="btn btn-danger mr-1"
>
<i className="fas fa-times" />
</button>
) : null}
</span>
) : null}
</div>
</div>
</div>
);
};
顺便说一句,不要与Redux文档中给出的示例作斗争。我认为对于新来者来说有点复杂。