前言:我们一般在输入框中输入文字,点击添加按钮实现;但有些人不想用鼠标去点击,直接输入完成后回车,也是可以实现的,这就是键盘事件。
一、键盘事件(回车...)
TodoInput 文件:
import React, { Component } from 'react'
export default class TodoInput extends Component {
static defaultProps={
btnText:'添加TODO'
}
constructor(){
super()
this.state={
inputValue:''
}
}
handleInputChange = (e) => {
this.setState({
inputValue:e.currentTarget.value
})
}
handleAddClick = (e) => {
console.log(e) //试试
this.props.addTodo(this.state.inputValue) //子组件向父组件传递
}
render() {
return (
<div>
<input
type="text"
value={this.state.inputValue}
onChange={this.handleInputChange}
onKeyUp={this.handleAddClick} //这里!!!
/>
<button onClick={this.handleAddClick}>
{this.props.btnText}
</button>
</div>
)
}
}
结果:在输入的时候会发现,它就已经获取了,并没有通过回车键,所以这样做是不对的。
TodoInput 文件:(正确写法)
import React, { Component } from 'react'
export default class TodoInput extends Component {
// static propTypes={
// btnText:propTypes.string
// }
static defaultProps={
btnText:'添加TODO'
}
constructor(){
super()
this.state={
inputValue:''
}
}
handleInputChange = (e) => {
this.setState({
inputValue:e.currentTarget.value
})
}
handleKeyUp = (e) => { //回车事件
if(e.keyCode === 13){
this.handleAddClick()
}
}
handleAddClick = (e) => {
this.props.addTodo(this.state.inputValue) //子组件向父组件传递
}
render() {
return (
<div>
<input
type="text"
value={this.state.inputValue}
onChange={this.handleInputChange} //点击事件
onKeyUp={this.handleKeyUp} //回车事件
/>
<button onClick={this.handleAddClick}>
{this.props.btnText}
</button>
</div>
)
}
}
结果回车、添加按钮都可以实现
二、ref
react 里面通过 ref 来获取组件或者 dom 元素
要使用 ref 之前必需先调 React.createRef 方法来创建一个 ref
在 constructor 里创建 ref,我们把创建的结果赋值给一个 this
import React, { Component,createRef } from 'react'
TodoInput 文件:
import React, { Component,createRef } from 'react'
export default class TodoInput extends Component {
// static propTypes={
// btnText:propTypes.string
// }
static defaultProps={
btnText:'添加TODO'
}
constructor(){
super()
this.state={
inputValue:''
}
this.inputDom=createRef() //创建ref
}
handleInputChange = (e) => {
this.setState({
inputValue:e.currentTarget.value
})
}
handleKeyUp = (e) => { //回车事件
if(e.keyCode === 13){
this.handleAddClick()
}
}
handleAddClick = (e) => {
//实际项目会对this.state.inputValue做验证,验证通过才继续执行下面的方法
if (this.state.inputValue === '') {
return
}
this.props.addTodo(this.state.inputValue) //子组件向父组件传递
this.setState({
inputValue:''
},() => {
this.inputDom.current.focus()
})
}
render() {
return (
<div>
<input
type="text"
value={this.state.inputValue}
onChange={this.handleInputChange} //点击事件
onKeyUp={this.handleKeyUp} //回车事件
ref={this.inputDom} //ref
/>
<button onClick={this.handleAddClick}>
{this.props.btnText}
</button>
</div>
)
}
}
三、删除事件
应该写在 App.js 文件里面:通过 TodoList 把事件继续往下传递
四、改变完成、未完成状态
TodoItem 文件:
import React, { Component } from 'react'
export default class TodoItem extends Component {
handleCheckboxChange = () =>{
// console.log('changed')
this.props.onCompeletedChange()
}
render() {
return (
<li>
<input
checked={this.props.isCompleted}
onChange={this.handleCheckboxChange}
type="checkbox"
/>
<span>{this.props.title}{this.props.isCompleted ? '完成' : '未完成'}</span>
</li>
)
}
}
App.js 文件:
import React, { Component } from 'react'
import{
TodoHeader,
TodoInput,
TodoList,
Like
} from './components'
export default class App extends Component {
// state = {
// title:'待办事项列表'
// }
constructor(){
super()
this.state={
title:'待办事项列表',
desc:'今日事,今日毕',
todos:[{
id:1,
title:'吃饭',
assignee:'FatBaby',
article:'<div>aaaaaa</div><i>bbbbb</i>',
isCompleted:true
},{
id:2,
title:'睡觉',
assignee:'HeiWa',
isCompleted:false
}]
}
}
addTodo = (TodoTitle) => { //TodoInput 里面去执行这个方法
console.log(TodoTitle)
// this.setState({
// todos:this.state.todos.concat({
// id:Math.random(),
// title:TodoTitle,
// isCompleted:false
// })
// })
const newTodos=this.state.todos.slice() //重新复制一份数组
newTodos.push({
id:Math.random(),
title:TodoTitle,
isCompleted:false
})
this.setState({
todos:newTodos
})
}
onCompeletedChange = () =>{
console.log('onCompeletedChange');
}
render() {
return (
<>
<TodoHeader desc={this.state.desc}>
{this.state.title}
</TodoHeader>
<TodoInput
addTodo={this.addTodo}
/>
<TodoList
todos={this.state.todos}
onCompeletedChange={this.onCompeletedChange}
/>
<Like />
</>
)
}
}
TodoList 文件:
import React, { Component } from 'react'
import TodoItem from './TodoItem'
import PropTypes from 'prop-types'
export default class TodoList extends Component {
static propTypes={
todos:PropTypes.arrayOf(PropTypes.shape({
id:PropTypes.number.isRequired,
title:PropTypes.string.isRequired,
isCompleted:PropTypes.bool.isRequired
})).isRequired,
onCompeletedChange:PropTypes.func
}
render() {
return (
<ul>
{
this.props.todos.map(todo => {
return(
// <TodoItem
// key={todo.id}
// id={todo.id}
// title={todo.title}
// assignee={todo.assignee}
// isCompleted={todo.isCompleted}
// />
<TodoItem
onCompeletedChange={this.props.onCompeletedChange}
key={todo.id}
{...todo}
/>
)
})
}
</ul>
)
}
}
现在做成可以点击的状态
App.js 文件:
import React, { Component } from 'react'
import{
TodoHeader,
TodoInput,
TodoList,
Like
} from './components'
export default class App extends Component {
constructor(){
super()
this.state={
title:'待办事项列表',
desc:'今日事,今日毕',
todos:[{
id:1,
title:'吃饭',
assignee:'FatBaby',
article:'<div>aaaaaa</div><i>bbbbb</i>',
isCompleted:true
},{
id:2,
title:'睡觉',
assignee:'HeiWa',
isCompleted:false
}]
}
}
addTodo = (TodoTitle) => { //TodoInput 里面去执行这个方法
const newTodos=this.state.todos.slice() //重新复制一份数组
newTodos.push({
id:Math.random(),
title:TodoTitle,
isCompleted:false
})
this.setState({
todos:newTodos
})
}
onCompeletedChange = (id) =>{
console.log('onCompeletedChange',id); //获取到id
this.setState((prevState) => { //可以点击按钮
return{
todos:prevState.todos.map(todo =>{
if(todo.id === id){
todo.isCompleted=!todo.isCompleted
}
return todo
})
}
})
}
render() {
return (
<>
<TodoHeader desc={this.state.desc}>
{this.state.title}
</TodoHeader>
<TodoInput
addTodo={this.addTodo}
/>
<TodoList
todos={this.state.todos}
onCompeletedChange={this.onCompeletedChange} //!!!!
/>
<Like />
</>
)
}
}
结果:是可以点击的状态了
那如果我这里没有传这个东西呢??
onCompeletedChange={this.onCompeletedChange}
报错是肯定的啊,那怎么改
那咱们去调用这个东西之前是不是应该去做一个判断
this.props.onCompeletedChange && this.props.onCompeletedChange(this.props.id)
一般我们会这样来写
const{
onCompeletedChange,
id
}=this.props
onCompeletedChange && onCompeletedChange(id)
或者
const{
onCompeletedChange = () => {},
id
}=this.props
onCompeletedChange(id)
}
最终代码(完整版)
TodoItem 文件:
import React, { Component } from 'react'
export default class TodoItem extends Component {
handleCheckboxChange = () =>{
// console.log('changed')
// this.props.onCompeletedChange && this.props.onCompeletedChange(this.props.id)
const{ //解构
onCompeletedChange = noop, //noop 其实就是一个空函数
id
}=this.props
onCompeletedChange(id)
}
render() {
const{ //解构,代码写起来更方便
isCompleted,
title
}=this.props
return (
<li>
<input
checked={isCompleted}
onChange={this.handleCheckboxChange}
type="checkbox"
/>
<span>{title}{isCompleted ? '完成' : '未完成'}</span>
</li>
)
}
}
App.js 文件:
import React, { Component } from 'react'
import{
TodoHeader,
TodoInput,
TodoList,
Like
} from './components'
export default class App extends Component {
// state = {
// title:'待办事项列表'
// }
constructor(){
super()
this.state={
title:'待办事项列表',
desc:'今日事,今日毕',
todos:[{
id:1,
title:'吃饭',
assignee:'FatBaby',
article:'<div>aaaaaa</div><i>bbbbb</i>',
isCompleted:true
},{
id:2,
title:'睡觉',
assignee:'HeiWa',
isCompleted:false
}]
}
}
addTodo = (TodoTitle) => { //TodoInput 里面去执行这个方法
console.log(TodoTitle)
// this.setState({
// todos:this.state.todos.concat({
// id:Math.random(),
// title:TodoTitle,
// isCompleted:false
// })
// })
const newTodos=this.state.todos.slice() //重新复制一份数组
newTodos.push({
id:Math.random(),
title:TodoTitle,
isCompleted:false
})
this.setState({
todos:newTodos
})
}
onCompeletedChange = (id) =>{
console.log('onCompeletedChange',id); //获取到id
this.setState((prevState) => { //点击按钮
return{
todos:prevState.todos.map(todo =>{
if(todo.id === id){
todo.isCompleted=!todo.isCompleted
}
return todo
})
}
})
}
render() {
return (
<>
<TodoHeader desc={this.state.desc}>
{this.state.title}
</TodoHeader>
<TodoInput
addTodo={this.addTodo}
/>
<TodoList
todos={this.state.todos}
// onCompeletedChange={this.onCompeletedChange}
/>
<Like />
</>
)
}
}
over