react+react-beautiful-dnd应用
效果预览 【工具实例|react+react-beautiful-dnd实例代办事项】
文章图片
实现思路
index.js入口文件配置
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import 'bulma-start/css/main.css' // 引入bulma样式
ReactDOM.render(
,
document.getElementById('root')
)
app.jsx主页面配置
- Provider格式
return(自己的代码
)
import React,{ Component} from "react";
import TodoHeader from './components/TodoHeader.jsx'
import TodoInput from "./components/TodoInput";
import TodoList from "./components/TodoList";
import TodoFoot from "./components/TodoFoot";
import {Provider} from "./untils/with-context" // 引入TodoContext组件export default class App extends Component{
state ={
todos:Array(6).fill(null).map((_,index)=>({
id:index++,
title:'待办事项'+index++,
completed: Math.random()>0.5,
}))
}
// 拖拽后的更新state处理函数
drag(newTodos){
this.setState({
todos:[...newTodos],
})}
// 添加事件处理函数
addTodoItem=title=>{
this.setState({
todos:[
...this.state.todos,
{
/* id:this.state.todos[this.state.todos.length-1]+1,
* 更新setState是异步的,这里是拿不到最新的state
*/
id:Math.random(),
title,
completed:false,
}
]
})}
// 删除事件处理函数
delTodo=id=>{
this.setState({
todos:this.state.todos.filter(todo=>todo.id !==id)
})
}
// 更改事件状态处理函数
changComple=id=>{
this.setState({
todos:this.state.todos.map(todo=>{
if(todo.id === id){
todo.completed=!todo.completed
}
return todo
})
})}
// 根据总选框状态设置每个单选框状态
allCheckbox=(status)=>{
this.setState({
todos:this.state.todos.map(todo=>{
todo.completed=status
return todo
})
})
}
// 删除已完成事件
delCompelted=()=>{
this.setState({
todos:this.state.todos.filter(todo=>!todo.completed)
})
}
render() {
return(
)
}
}
untils/with-context.js封装工具todoContext
import {createContext} from "react";
// 创建creatContext对象
const TodoContext = createContext()
// 结构要用到的React组件
const {
Provider, // 生产组件
Consumer, // 消费组件
} = TodoContext
export {
Provider,
Consumer,
TodoContext,
}
- components/TodoHeader.jsx页面头部
import React, { Component } from 'react'export default class TodoHeader extends Component {
render() {
return (待办事项列表
)
}
}
components/TodoInput.jsx该文件主要负责添加事件
import React, {Component, createRef} from "react";
export default class TodoInput extends Component{
state={
inputValue:'输入代办事件', // 定义input输入框内容
}
inputRef=createRef() // 定义ref绑定DOM元素,作用是为下面自动获取焦点做准备
// 输入框中输入的内容设置给state作用1:输入框内容改变2:后面提交添加事件拿到input内容
handleChang=Event=>{
this.setState({
inputValue:Event.target.value
})
}
// 添加代办事件
handleDown=Event=>{
// 验证下是否为空
if(this.state.inputValuehttps://www.it610.com/article/==='' || this.state.inputValue=https://www.it610.com/article/==null) return
if(Event.keyCode ===13){
this.add()
}
}
// 添加处理函数
add=()=>{
// add方法通过props从App传入
this.props.add(this.state.inputValue)
this.state.inputValue=''
// ref绑定后通过inputRef.current拿到DOM元素
this.inputRef.current.focus()
}
render() {
return(
)
}
}
介绍下react-beautiful-dnd处理函数
- 官方解析图
文章图片
- 格式DragDropContext最外面盒子Droppable第二层盒子
{provided=>(
<*
ref={provided.innerRef}
{...provided.droppableProps}
// 官方固定格式
>
自己的代码
<*/>
{provided.placeholder}
)}
- 格式Draggable最里面盒子
{provided=>(
<*
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
自己的代码
<*/>
{provided.placeholder}
)}
- 一但移动(从第5个事件移动到第4个事件)onDragEnd回调函数打印结果console.log(result)
{draggableId: '5', type: 'DEFAULT', source: {…}, reason: 'DROP', mode: 'FLUID', …}
combine: null
destination: {droppableId: 'columns', index: 4} // 移动到第4个事件
draggableId: "5"
mode: "FLUID"
reason: "DROP"
source: {index: 5, droppableId: 'columns'} // 移动的第5个事件
type: "DEFAULT"
[[Prototype]]: Object
components/TodoList.jsx
import React,{Component} from "react";
import TodoItem from "./TodoItem";
import PropTypes from 'prop-types'
import {DragDropContext} from 'react-beautiful-dnd'
import {Droppable} from 'react-beautiful-dnd'export default class TodoList extends Component{
// 类型检查
static propTypes={
todos:PropTypes.array.isRequired,
}
// 默认值
static defaultProps = {
todos: [],
}
// 根据choice数值决定渲染事项
state={
choice:1
}
// react-beautiful-dnd核心处理函数,负责交换后的处理(可以自定义)
onDragEnd=result=>{
console.log(result)
const {destination,source,draggableId}=result
if(!destination){ // 移动到了视图之外
return
}
if( // 移动到原来位置,也就是位置不变
destination.droppableId===source.droppableId &&
destination.index===source.index
){ return;
}
const newTaskIds=Array.from(this.props.todos) // 转化为真正的数组
newTaskIds.splice(source.index,1) // 删除移动的数组
newTaskIds.splice(destination.index,0,this.props.todos[source.index]) // 在移动到的位置初放置被删除的数组// 调用App文件中的drag执行交换后的更改
this.props.drag(newTaskIds)}
// 点击时渲染不同DOM
choice=(num)=>{
this.setState({
choice:num
})
}
render() {
let uls=null
if(this.state.choice===1){
uls=(
{provided=>(
{this.props.todos.length>0
? this.props.todos.map((todo,index)=>{
return (
)
})
:添加代办事项}
{provided.placeholder}
)} )
}else if(this.state.choice===2){
// 过滤下事件
let newtodos=this.props.todos.filter(todo=> todo.completed)
uls=(
{provided=>(
{newtodos.length>0
? newtodos.map((todo,index)=>{
return (
)
})
:暂无已完成事件}
{provided.placeholder}
)} )
}else if(this.state.choice===3){
// 过滤下事件
let newtodos=this.props.todos.filter(todo=> !todo.completed)
uls=(
{provided=>(
{newtodos.length>0
? newtodos.map((todo,index)=>{
return (
)
})
:暂无未完成事件}
{provided.placeholder}
)} )
}
return(
<>this.choice(1)}>所有
this.choice(2)}>已完成
this.choice(3)}>未完成
{uls}
>
)
}
}
components/TodoFoot.jsx
- Consumer格式
return(
{value=>{
const {结构要用的属性或方法的名字} = value
return(
自己的代码,value中含有Provider中传入的所有值
)
}}
)
import React,{Component} from "react";
import {Consumer} from '../untils/with-context'
export default class TodoFoot extends Component{
render() {
return(
{
value => {
const {allCheckbox,todos,delCompelted} = value
const completedNum =todos.filter(todo=>todo.completed).length
const AllChecked =todos.length?todos.every(todo=>todo.completed):false
return(
)
}
}
)
}
}
package.json中的三方包资源
{
"name": "react-demo",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"bulma-start": "^0.0.5",
"react": "^17.0.2",
"react-beautiful-dnd": "^13.1.0",
"react-dom": "^17.0.2",
"react-native": "^0.68.2",
"react-scripts": "4.0.3",
"redux-persist": "^6.0.0",
"store": "^2.0.12",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
推荐阅读
- react|react3-Redux
- java|职教云自动选题、答题并自动修改答题已用时间 脚本
- 网页设计|前端网页设计期末大作业-写真工作室网站(资源链接在文末)
- Vue|Vue核心?(生命周期)
- Vue|Vue中的过滤器
- Vue|Vue核心?(内置指令)
- WEB|我的VUE 学习之路(下)
- Axios|axios的配置对象说明(config)
- HTML + CSS + JavaScript 实现打地鼠小游戏 闲暇时刻玩一玩 轻松丢烦恼~