一、模块化
模块化即将每个功能区划分出来,典型的mvc框架的代码组织方式,目录结构如下:
同样,将react-redux项目进行模块化编写可以更方便我们的操作,。
二、待办事项列表项目
1.项目大概
1)可以显示所有代办事项 2)选择显示完成事项 3)选择显示未完成事项 4)点击删除,删除当前事项 5)点击待办事项内容,可以在完成和未完成间转换,画了横线表示完成了 6)上方可以添加代办事项
2.基本框架
在src下创建两个功能区,filter用来筛选显示状态;todolist用来做待办事项表的基本操作。 两个功能区都有一个对外接口,index.js文件,这里会将功能区内所有组件、函数等导入到此文件中,这样,功能区外访问时只需在index路径下访问即可。 外部文件index.js根据两个功能区导出的reducer合并后创建store(reducer的合并使用到了combineReducers()函数,具体使用如下图: ) 第一步框架写完。
3.todoList基本操作分析实现
对于一个待办事项表,我们需要分析一下三点基本操作: 1.添加 添加一个代办事项,相当于更改当前todoList的状态,对于redux更改状态,我们首先想到的是同步更改状态方法dispatch。 首先,在todoList功能区中的reducer文件下设置更改状态的逻辑下(因为两个功能区初始状态不同,所以初始状态直接在reducer中设置,如下rodoList的初始状态为空数组[]): 在这里详细解释,我们todoList列表应该包含三个属性值:id,text,completed分别表示事项id(便于以后的操作),文本内容,是否完成。三项存于一个数组中todos=[id,text,completed],而action中存储的对象为{type:"",todos:[]},所以reducer中逻辑如上。 到这里,如果我们在外部index文件中使用:
结果如下: 这里不免想到,id和text是一个变化的属性,不如定义一个方法addTodos去实现,于是在todoList功能区创建action文件,用来表示三种基本操作的函数,首先还是addTodos(只需直接return一个dispatch方法的参数对象{type:"",todos:""}):
var id=0;
const addTodos=function(text){
return {
type:"ADD_TODOS",
todos:{
id:id++,
text:text,
completed:false
}
}
}
export {addTodos}
然后store.dispatch(addTodos("第一件事")) 调用即可,当然在todoList功能区内,addTodos还是要先引入index在对外暴露。
2.转换(完成<=>未完成) 思路:根据当前选中的id号进行切换,首先在功能区内设置相关行为函数即获取id: 打印输出获取到的结果: 之后编写reducer中的改变状态的逻辑(调用一个map方法,即可以返回一个新的数组,遍历state对象中每一个个数组,如果当前数组id与action(即要改变的)的id一致,将completed取反,覆盖原来的item数组值;否则直接返回item数组): 调用console.log(store.dispatch(toggleTodos(2))) 输出: 3.删除 思路:根据id删除,又是根据id,可以发现基本思路和切换差不多,我们只需对比找到id项,然后去掉即可,于是我们想到了filter方法,reducer逻辑如下(返回一个数组,返回的条件是item.id!=action.id即不是当前选中的id); 附加上action上的逻辑: 基本操作和上一步一样。 调用console.log(store.dispatch(delTodos(1))) 结果:
4.filter基本方法实现
filter功能区是页面点击时触发的相关方法,有三种状态:ALL,Completed,Uncompleted,即页面显示的三个按钮,我们要做的就是,点击按钮,筛选出要显示的数据。 首先,页面初始化state=“ALL”,初始化写在reducer中,原因开头讲过。 然后编写action文件,这个action文件是要传入到dispatch作为参数对象的,所以action文件中函数return一个{type:"",filter:""},具体如下:
const changeFilter=function(filter){
return {
type:"CHANGE_FILTER",
filter:filter
}
}
export {changeFilter}
将其导入到index中,对外暴露。 action文件写完之后,我们将这种变化通过dispatch传递到filter的reducer中,根据type判断进行什么逻辑,所以具体reducer中逻辑如下:
const reducer=function(state="ALL",action){
switch(action.type){
case "CHANGE_FILTER":{
return action.filter
}
default:{
return state
}
}
}
export default reducer
因为filter的初始化state=“ALL",如果想将其改成其他状态,直接return action中的filter即可。最后 调用store.dispatch(changeFilter("COMPLETED")) 控制台输出结果;
5.视图
1.ToDo组件视图
使用react-redux编写视图部分,首先下载`npm i react-redux --save` 在index文件下引入Provider组件,传入参数store,具体使用见 [React复习(4):React-Redux](https://blog.csdn.net/qq_45856669/article/details/123226329?spm=1001.2014.3001.5502) 然后在TodoList文件下引入connect,规定需要的参数todos,然后使用ul+map渲染,基本js操作。 具体渲染如下:
import React,{Component} from 'react'
import {connect} from 'react-redux'
class TodoList extends Component{
constructor(props){
super(props)
}
render(){
console.log(this.props)
const {todos}=this.props
return(
<div>
<ul>
{todos.map((item,index)=>{
return <li key={index}>{item.text}</li>
})}
</ul>
</div>
)
}
}
const mapStateToProps=function(state) {
return {
todos:state.todos
}
}
export default connect(mapStateToProps)(TodoList)
渲染结果: 然后渲染完成事件的效果啊: 结果如图: 项目要求根据filter进行显示数据,所以在这里我们还要判断filter,所以自定义函数getTodos(state.todos,state.filter)用来获取我们最终需要渲染的数据给到mapStateToProps函数中,这个函数中就是存放我们需要获取的数据。 具体代码如下(使用filter进行筛选):
const getTodos=function(todos,filter){
switch(filter){
case ALL:{
return todos
}
case COMPLETED:{
return todos.filter((item,index)=>{
return item.completed==true
})
}
case UNCOMPLETED:{
return todos.filter((item,index)=>{
return item.completed==false
})
}
default:{
return "???"
}
}
}
const mapStateToProps=function(state) {
return {
todos:getTodos(state.todos,state.filter)
}
}
结果: 添加点击事件 点击文本切换事件,和删除按钮的点击事件,我们都在todolist功能区下的action文件下写过,想要调用这些方法,我们就要访问存储他们的地方store,所以通过mapDispatchToProps函数获取这两个函数,获取代码:
const mapDispatchToProps=function(dispatch){
return{
onToggleTodos(id){
dispatch(toggleTodos(id))
},
onDelTodos(id){
dispatch(delTodos(id))
}
}
}
其实就是创建函数,函数内部调用store中的逻辑函数,最后记得添加到connect里,然后就是onClick事件的添加: 两种操作均是根据id进行,所以传入参数id即可,而map中传入item.id即可,最终实现效果。 输入框添加事件 另外设置一个组件,实现添加事件操作。 和TodoList组件相似,需要store的addTodos函数,同是再定义一个mapDispatchToProps,通过connect连接,(注意:mapDispatchToProps是connect的第二个参数,这里不需要第一个参数但也要传递,可以直接传递null)onClick调用即可,组件代码如下:
import React,{Component} from 'react'
import {connect} from 'react-redux'
import { addTodos} from '../action';
class AddTodos extends Component{
constructor(props){
super(props)
}
render(){
const {onAddTodos}=this.props
return (
<div>
<input type="text" className="int" />
<button onClick={()=>{onAddTodos(document.getElementsByClassName("int")[0].value)}}>ADD</button>
</div>
)
}
}
const mapDispatchToProps=function(dispatch) {
return {
onAddTodos(text){
dispatch(addTodos(text))
}
}
}
export default connect(null,mapDispatchToProps)(AddTodos)
结果显示: 应模块化要求,接口统一,在views下再建一个ToDo文件,
import React,{Component} from 'react'
import {connect} from 'react-redux'
import AddTodos from './AddTodos';
import TodoList from './TodoList'
class ToDo extends Component{
constructor(props){
super(props)
}
render(){
return (
<div>
<AddTodos></AddTodos>
<TodoList></TodoList>
</div>
)
}
}
export default ToDo
//这里获取input的value值有很多种方式(ref、onChange。。。)
2.Filter组件视图
渲染三个按钮,connect传参,定义mapDispatchToProps函数(
注意注意注意:已经忘了很多遍的事情,mapDispatchToProps定义完不要忘了写到connect中,还有connect第一个参数的传递不要忘了) 组件代码如下:
import React,{Component} from 'react'
import {CHANGE_FILTER} from '../actionType'
import {connect} from 'react-redux'
import {changeFilter} from '../action'
import {ALL,COMPLETED,UNCOMPLETED} from '../../contains'
class Filter extends Component{
constructor(props){
super(props)
}
render(){
const {change_Filter}=this.props
return(
<div>
<button onClick={()=>{change_Filter(ALL)}}>All</button>
<button onClick={()=>{change_Filter(COMPLETED)}}>Completed</button>
<button onClick={()=>{change_Filter(UNCOMPLETED)}}>Uncompleted</button>
</div>
)
}
}
const mapDispatchToProps=function(dispatch) {
return {
change_Filter(e){
dispatch(changeFilter(e))
}
}
}
export default connect(null,mapDispatchToProps)(Filter)
三、console.log(“END”)
总体过程挺乱的,主要模块化分了太多文件,本项目比较小,所以会感觉非模块化比模块化简单,但对于大项目这个真的很重要。 项目文件如下:
链接:https://pan.baidu.com/s/1psOTedMeYIpHqKk5oZ46jw
提取码:0hgw
|