Javascript 如何将React组件分为表示组件和容器组件
对于react和redux仍然是新手,并且一直在开发一个MERN用户注册应用程序,我现在正在开发该应用程序 在redux文档中,我发现创建者建议在将redux与react集成时将其代码分为两类组件:与事物外观有关的表示问题和与事物工作方式有关的容器问题。看 我认为这将允许更好的管理和应用程序的可伸缩性 对于不熟悉的人,这里有一个很好的优势解释: 我只是努力掌握这个概念,并以这种方式重写代码 下面是我编写的用于显示用户创建的注释的post组件的示例。它正在接收作为道具传递的更高级别组件中的post的数据。作为回报,我应用了所有带有引导样式的标记。我通过创建事件处理程序订阅导入并使用的redux操作Javascript 如何将React组件分为表示组件和容器组件,javascript,reactjs,react-redux,components,mern,Javascript,Reactjs,React Redux,Components,Mern,对于react和redux仍然是新手,并且一直在开发一个MERN用户注册应用程序,我现在正在开发该应用程序 在redux文档中,我发现创建者建议在将redux与react集成时将其代码分为两类组件:与事物外观有关的表示问题和与事物工作方式有关的容器问题。看 我认为这将允许更好的管理和应用程序的可伸缩性 对于不熟悉的人,这里有一个很好的优势解释: 我只是努力掌握这个概念,并以这种方式重写代码 下面是我编写的用于显示用户创建的注释的post组件的示例。它正在接收作为道具传递的更高级别组件中的post
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);
但我不知道如何在实践中实现这一点。
如果你能帮我解释,并提供一些例子代码,这将是惊人的
提前谢谢 我总是将类似html的表示代码放在另一个文件中,他们称之为无状态组件 关键组件是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);
这与@jsDevia的答案非常相似,但我不会在这里创建单独的组件,因为您说过您的Post组件已经连接到Redux。因此,您可以抓取所有动作创建者,并在那里声明,然后将其传递给PostItem组件 第二个区别是我使用功能组件而不是类组件,因为这里不需要任何状态或生命周期方法 第三个区别很小。我删除了onClick处理程序中的所有绑定。对于这个作用域问题,我将为处理程序使用箭头函数。同样,我们不需要任何参数,比如post.\u id来传递这些函数,因为这里已经有post作为道具了。这就是分离我们的组件的美妙之处 在回调处理程序中使用bind或arrow函数会导致具有大量组件(如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文档中给出的示例为难。我认为这对新手来说有点复杂 @jsDevia的答案很好,虽然我不会在小应用程序的onClick中使用箭头函数,但我认为这对小应用程序来说不是问题。我想知道,您的Post组件是否连接到Redux?是的,我的Post组件也连接到Redux。您是否建议只将最高级别的组件连接到redux?如果此组件可以是所有其他呈现组件的容器组件,那么我可以建议这样做@jsDevia的答案与此逻辑类似,但他使用了另一个组件。但是,您已经有一个Post组件,它已连接到Redux。感谢您的回答!因此,基本上,您将呈现组件嵌套在容器组件中,并传递道具。这是如何处理此问题的最佳实践吗?在文档中,我看到了使用connect和mapDispatchToProps的不同方法。这到底是如何工作的?是的,这是我的解决方案,通过这种方式,您可以以适当的方式管理不同的逻辑。我一直在按照您建议的格式更改我的所有组件,并且工作得非常完美。谢谢!是否有理由只在容器组件中使用道具类型?根据本文,道具类型在组件中得到验证,因此如果出现问题,组件可能会大声失败。这到底是如何工作的?我应该在容器中只使用道具类型还是同时使用这两个文件?非常感谢,这使它更干净!此外,我还阅读了使用作为箭头函数编写的事件处理程序仍然属于实验语法。这会影响性能吗?它不需要函数绑定,因此可以在没有构造函数的情况下作为函数组件编写。我不知道这是一个折衷方案还是使用起来确实安全。箭头函数在传输时比常规函数稍微慢一些。但我不认为这有什么区别。顺便说一下,类或函数分离不是这样的。如果您不需要状态或任何生命周期方法,您可以使用并且应该使用功能组件。我们不使用类组件,因为我们必须绑定我们的函数。这不是原因:我们使用类组件,因为我们需要一个状态或生命周期方法,我们在这个组件中绑定我们的函数,因为我们需要在回调中使用它。功能组件不存在这些问题。感谢您的明确解释。是的,除了可读性更高的代码外,使其无状态对我来说是一个挑战 f希望使用功能组件的原因。这样我就不会在没有太多数据的情况下意外更改状态。
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>
);
};