如何使用 Hooks 转换基于 class 的 React-Redux 代码
How to transform class-based React-Redux code using Hooks
我正在学习 React/Redux,我正在尝试将此代码从基于 class 重构为 functional/hooks-based 代码。该应用程序是我正在做的一个练习,它有三个组件 Posts.js,我从 typicode.com 中获取 post 的列表。获取列表中的每个 post 都有一个按钮被攻击。
在 onClick 上,它应该显示每个 post(PostDetails.js 和 Comments.js)的详细信息:
目前,帖子和评论都是基于 class 的组件。我需要:
第 1 步:将它们更改为功能组件并使用 React Hooks,但仍保留 connect()、mapStateToProps 和 mapDispatchToProps;
第 2 步:实施 React-Redux 挂钩(UseSelector、useDispatch)
App.js
//imports...
const App = () => {
return (
<div className="container">
<div><Posts /></div>
<div><PostDetails /></div>
</div>
)
}
export default App;
动作
import jsonPlaceholder from '../apis/jsonPlaceholder';
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
dispatch({type: 'FETCH_POSTS', payload: response.data})
};
export const selectPost = post => {
return ({
type: 'POST_SELECTED',
payload: post
})
}
export const fetchComments = (id) => async dispatch => {
const response = await jsonPlaceholder.get(`/comments?postId=${id}`);
dispatch({type: 'FETCH_COMMENTS', payload: response.data})
}
减速机
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_POSTS':
return action.payload;
default:
return state;
}
}
export default (selectedPost = null, action) => {
if (action.type === 'POST_SELECTED') {
return action.payload;
}
return selectedPost;
}
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_COMMENTS':
return action.payload;
default:
return state;
}
}
export default combineReducers({
posts: postsReducer,
selectedPost: selectedPostReducer,
comments: commentsReducer
})
components/Posts.js
import React from 'react';
import { connect } from 'react-redux';
import { fetchPosts, selectPost } from '../actions';
import '../styles/posts.scss';
class Posts extends React.Component {
componentDidMount() {
this.props.fetchPosts()
}
renderPosts() {
return this.props.posts.map(post => {
if (post.id <= 10)
return (
<div className='item' key={post.id}>
<div className="title">
<h4>{post.title}</h4>
</div>
<button
onClick={() => {
this.props.selectPost(post)
console.log(post)
}
}>Open</button>
<hr/>
</div>
)
})
}
render() {
return(
<div className="list">
{ this.renderPosts() }
</div>
)
}
}
const mapStateToProps = state => {
return {
posts: state.posts,
selectedPost: state.post
}
};
const mapDispatchToProps = {
fetchPosts,
selectPost
}
export default connect(mapStateToProps, mapDispatchToProps)(Posts);
components/PostDetails.js
import React from 'react';
import { connect } from 'react-redux';
import Comments from './Comments'
const PostDetails = ({ post }) => {
if (!post) {
return <div>Select a post</div>
}
return (
<div className="post-details">
<div className="post-content">
<h3>{post.title}</h3>
<p>{post.body}</p>
<hr/>
</div>
<div className="comments-detail">
<Comments postId={post.id}/>
</div>
</div>
)
}
const mapStateToProps = state => {
return {post: state.selectedPost}
}
export default connect(mapStateToProps)(PostDetails);
components/Comments.js
import React from 'react';
import { connect } from 'react-redux';
import { fetchComments } from '../actions'
class Comments extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.postId && this.props.postId !== prevProps.postId){
this.props.fetchComments(this.props.postId)
}
}
renderComments() {
console.log(this.props.comments)
return this.props.comments.map(comment => {
return (
<div className="comment" key={comment.id}>
<div className="content">
<h5>{comment.name}</h5>
<p>{comment.body}</p>
</div>
<hr />
</div>
)
})
}
render() {
return (
<div className="comments">
{this.renderComments()}
</div>
)
}
}
const mapStateToProps = state => {
return {comments: state.comments}
}
export default connect(mapStateToProps, {fetchComments})(Comments);
这可能是一种创建 Posts
组件的方法:
我假设当您 dispatch fetchPosts()
操作时,您正在使用 Redux 中的 reducers 保存其响应。
而且,您不需要 fetchedPosts
处于本地组件状态,因为您已经在 Redux 状态中拥有此数据。
const Posts = () => {
const posts = useSelector((state) => state.posts)
const dispatch = useDispatch()
// const [fetchedPosts, setFetchedPosts] = useState([]) // NOT needed
useEffect(() => {
dispatch(fetchPosts())
// setFetchedPosts(posts) // NOT needed
// console.log(posts) // NOT needed, its value may confuse you
}, [])
// Do this, if you want to see `posts` in browser log
useEffect(() => {
console.log(posts)
}, [posts])
/* NOT needed
const renderPosts = () => {
posts.map((post) => {
console.log(post)
})
} */
return (
<>
{posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</>
)
}
export default Posts
我正在学习 React/Redux,我正在尝试将此代码从基于 class 重构为 functional/hooks-based 代码。该应用程序是我正在做的一个练习,它有三个组件 Posts.js,我从 typicode.com 中获取 post 的列表。获取列表中的每个 post 都有一个按钮被攻击。 在 onClick 上,它应该显示每个 post(PostDetails.js 和 Comments.js)的详细信息:
目前,帖子和评论都是基于 class 的组件。我需要:
第 1 步:将它们更改为功能组件并使用 React Hooks,但仍保留 connect()、mapStateToProps 和 mapDispatchToProps;
第 2 步:实施 React-Redux 挂钩(UseSelector、useDispatch)
App.js
//imports...
const App = () => {
return (
<div className="container">
<div><Posts /></div>
<div><PostDetails /></div>
</div>
)
}
export default App;
动作
import jsonPlaceholder from '../apis/jsonPlaceholder';
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
dispatch({type: 'FETCH_POSTS', payload: response.data})
};
export const selectPost = post => {
return ({
type: 'POST_SELECTED',
payload: post
})
}
export const fetchComments = (id) => async dispatch => {
const response = await jsonPlaceholder.get(`/comments?postId=${id}`);
dispatch({type: 'FETCH_COMMENTS', payload: response.data})
}
减速机
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_POSTS':
return action.payload;
default:
return state;
}
}
export default (selectedPost = null, action) => {
if (action.type === 'POST_SELECTED') {
return action.payload;
}
return selectedPost;
}
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_COMMENTS':
return action.payload;
default:
return state;
}
}
export default combineReducers({
posts: postsReducer,
selectedPost: selectedPostReducer,
comments: commentsReducer
})
components/Posts.js
import React from 'react';
import { connect } from 'react-redux';
import { fetchPosts, selectPost } from '../actions';
import '../styles/posts.scss';
class Posts extends React.Component {
componentDidMount() {
this.props.fetchPosts()
}
renderPosts() {
return this.props.posts.map(post => {
if (post.id <= 10)
return (
<div className='item' key={post.id}>
<div className="title">
<h4>{post.title}</h4>
</div>
<button
onClick={() => {
this.props.selectPost(post)
console.log(post)
}
}>Open</button>
<hr/>
</div>
)
})
}
render() {
return(
<div className="list">
{ this.renderPosts() }
</div>
)
}
}
const mapStateToProps = state => {
return {
posts: state.posts,
selectedPost: state.post
}
};
const mapDispatchToProps = {
fetchPosts,
selectPost
}
export default connect(mapStateToProps, mapDispatchToProps)(Posts);
components/PostDetails.js
import React from 'react';
import { connect } from 'react-redux';
import Comments from './Comments'
const PostDetails = ({ post }) => {
if (!post) {
return <div>Select a post</div>
}
return (
<div className="post-details">
<div className="post-content">
<h3>{post.title}</h3>
<p>{post.body}</p>
<hr/>
</div>
<div className="comments-detail">
<Comments postId={post.id}/>
</div>
</div>
)
}
const mapStateToProps = state => {
return {post: state.selectedPost}
}
export default connect(mapStateToProps)(PostDetails);
components/Comments.js
import React from 'react';
import { connect } from 'react-redux';
import { fetchComments } from '../actions'
class Comments extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.postId && this.props.postId !== prevProps.postId){
this.props.fetchComments(this.props.postId)
}
}
renderComments() {
console.log(this.props.comments)
return this.props.comments.map(comment => {
return (
<div className="comment" key={comment.id}>
<div className="content">
<h5>{comment.name}</h5>
<p>{comment.body}</p>
</div>
<hr />
</div>
)
})
}
render() {
return (
<div className="comments">
{this.renderComments()}
</div>
)
}
}
const mapStateToProps = state => {
return {comments: state.comments}
}
export default connect(mapStateToProps, {fetchComments})(Comments);
这可能是一种创建 Posts
组件的方法:
我假设当您 dispatch fetchPosts()
操作时,您正在使用 Redux 中的 reducers 保存其响应。
而且,您不需要 fetchedPosts
处于本地组件状态,因为您已经在 Redux 状态中拥有此数据。
const Posts = () => {
const posts = useSelector((state) => state.posts)
const dispatch = useDispatch()
// const [fetchedPosts, setFetchedPosts] = useState([]) // NOT needed
useEffect(() => {
dispatch(fetchPosts())
// setFetchedPosts(posts) // NOT needed
// console.log(posts) // NOT needed, its value may confuse you
}, [])
// Do this, if you want to see `posts` in browser log
useEffect(() => {
console.log(posts)
}, [posts])
/* NOT needed
const renderPosts = () => {
posts.map((post) => {
console.log(post)
})
} */
return (
<>
{posts.map((post) => (
<div key={post.id}>{post.title}</div>
))}
</>
)
}
export default Posts