React+Redux实现小书评论实战(一~三)
个人github: 个人github
github地址:项目
项目结构树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| React小书评论功能(一~三) ├─ README.md └─ src ├─ CommentApp.js ├─ components │ ├─ comment.css │ ├─ Comment.js │ ├─ CommentInput.js │ └─ CommentList.js ├─ componentsUI │ └─ CommentInputUI.js ├─ index.js └─ store ├─ actionCreators.js ├─ actionTypes.js ├─ index.js └─ reducer.js
|
用 Redux 状态管理实现了 React 小书的评论功能(一~三),同时基于企业级项目开发要求进行了整体优化,包括 actionTypes 和 actionCreator ,以及 UI 界面与业务逻辑分离。项目结构如上述所示,项目整体样式未经过调整,主要采用了阿里开源的 Ant Design 作为主要样式。
项目文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import React, { Component } from 'react'; import CommentInput from "./components/CommentInput" import CommentList from './components/CommentList';
class CommentApp extends Component { constructor(props) { super(props); this.state = {} } render() { return ( <div> <CommentInput /> <CommentList /> </div> ); } } export default CommentApp;
|
CommentInput 业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| import React, { Component } from 'react'; import store from "../store/index" import { inputChangeAction, textAreaChangeAction, commentSubmitAction } from "../store/actionCreators" import { CommentInputUI } from "../componentsUI/CommentInputUI"
class CommentInput extends Component { constructor(props) { super(props); this.state = store.getState() this.storeChange = this.storeChange.bind(this) store.subscribe(this.storeChange) this.inputChange = this.inputChange.bind(this) this.textAreaChange = this.textAreaChange.bind(this) this.commentSubmit = this.commentSubmit.bind(this) } storeChange() { this.setState(store.getState()) } inputChange(e) { const action = inputChangeAction(e.target.value) store.dispatch(action) } textAreaChange(e) { const action = textAreaChangeAction(e.target.value) store.dispatch(action) } commentSubmit() { const action = commentSubmitAction() store.dispatch(action) } render() { return ( <div> <CommentInputUI inputChange={this.inputChange} textAreaChange={this.textAreaChange} commentSubmit={this.commentSubmit} /> </div> ) } } export default CommentInput;
|
CommentInput UI界面 + 无状态组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import "antd/dist/antd.css" import { Input, Button } from 'antd' import "../components/comment.css"
const { TextArea } = Input; export const CommentInputUI = (props) => { return ( <div> <div className="inputRow"> <span>用户名:</span> <Input style={{ width: "300px", marginLeft:"20px" }} placeholder="Please enter your name" onChange={props.inputChange} /> </div> <div className="inputRow secondRow"> <span>评论内容:</span> <TextArea rows={5} style={{ width: "285px", marginLeft:"20px" }} onChange={props.textAreaChange} /> </div> <div> <Button type="primary" onClick={props.commentSubmit} >发布</Button> </div> </div> ); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import React, { Component } from 'react'; import store from "../store/index" import Comment from "./Comment"
class CommentList extends Component { constructor(props) { super(props); this.state = store.getState() this.storeChange = this.storeChange.bind(this) store.subscribe(this.storeChange) } storeChange() { this.setState(store.getState()) } render() { return ( <div> <div> {this.state.list.map( (item, index) => (<Comment content={item} index={index} key={index} />) )} </div> </div> ); } } export default CommentList;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import React, { Component } from 'react'; import "antd/dist/antd.css" import { Button } from 'antd' import store from '../store'; import { deleteCommentAction } from "../store/actionCreators"
class Comment extends Component { constructor(props) { super(props); this.deleteComment = this.deleteComment.bind(this); } deleteComment(index) { const action = deleteCommentAction(index) store.dispatch(action) } render() { return ( <div> <div> {this.props.content} <Button type="link" onClick={() => (this.deleteComment(this.props.index))} >删除</Button> </div> </div> ); } } export default Comment;
|
Store
src/store/actionTypes.js
action.type 常量单独分离
1 2 3 4 5
| export const INPUT_CHANGE = "input_change" export const TEXT_AREA_CHANGE = "text_area_change" export const COMMENT_SUBMIT = "comment_submit" export const DELETE_COMMENT = "delete_comment"
|
src/store/actionCreators.js
action 对象单独分离管理,用函数方式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { INPUT_CHANGE, TEXT_AREA_CHANGE, COMMENT_SUBMIT, DELETE_COMMENT } from "./actionTypes" export const inputChangeAction = (value) => ({ type: INPUT_CHANGE, value, }) export const textAreaChangeAction = (value) => ({ type: TEXT_AREA_CHANGE, value, }) export const commentSubmitAction = () => ({ type: COMMENT_SUBMIT, }) export const deleteCommentAction = (index) => ({ type: DELETE_COMMENT, index, })
|
src/store/index.js
store仓库创建
1 2 3 4 5
| import {createStore} from 'redux' import reducer from './reducer'
const store = createStore(reducer) export default store
|
src/store/reducer.js
reducer 管理,定义 action 操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import { act } from "react-dom/test-utils"
const defaultState = { inputValue: "", textAreaValue: "", list: [], }
export default (state = defaultState, action) => { if (action.type === "input_change") { let newState = JSON.parse(JSON.stringify(state)) newState.inputValue = action.value return newState } if (action.type === "text_area_change") { let newState = JSON.parse(JSON.stringify(state)) newState.textAreaValue = action.value return newState } if (action.type === "comment_submit") { let newState = JSON.parse(JSON.stringify(state)) newState.list.push(`${newState.inputValue}: ${newState.textAreaValue}`) return newState } if (action.type === "delete_comment") { let newState = JSON.parse(JSON.stringify(state)) newState.list.splice(action.index,1) return newState } return state }
|