尚硅谷2021版React技术全家桶全套完整版(零基础入门到精通/男神天禹老师亲授)
在看了老师的讲解后,自己打了一遍,以下总结了一下老师说的注意点和自己编写代码时注意点,后面附上自己写的代码。
流程
1. 创建项目并启动
-
全局安装 npm i/yarn -g create-react-app -
创建项目 create-react-app 项目名称 -
启动项目 npm start/yarn start
2. 拆分组件、实现静态组件
原生HTML转化为ReacDom时注意以下问题:
- 替换 class= 为 className=
- 替换style = “…” 为 {{…}} 且注意内部引号问题
- 标签闭和" / ",尤其是input、br
3.实现动态组件
-
组件存放位置 【单个】组件使用:放在其自身的state中 【多个】组件使用:放在共同的父组件state中(状态提升) -
状态在哪里,操作状态的方法就在哪里 -
父子通信: 【父】→【子】:通过props传递 【子】→【父】:通过props传递,在子组件中定义函数并将数据作为参数传递出去 -
自动生成唯一标识符id:nanoid,详情请看https://github.com/ai/nanoid -
注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
App.jsx
import React, { Component } from 'react'
import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer'
import './App.css'
export default class App extends Component {
state = {
todos: [
{ id: '001', name: '吃饭', done: true },
{ id: '002', name: '睡觉', done: true },
{ id: '003', name: '打代码', done: false },
{ id: '004', name: '逛街', done: false }
]
}
addTodoObj = (newObj) => {
const { todos } = this.state
const newTodos = [newObj, ...todos]
this.setState({ todos: newTodos })
}
updateTodos = (id) => {
console.log(id)
this.setState({
todos: this.state.todos.map(item => {
return item.id === id ? { ...item, done: !item.done } : item
})
})
}
deleteTodos = (id) => {
this.setState({
todos: this.state.todos.filter(item => item.id !== id)
})
}
deleteAllDoneTodo = () => {
this.setState({
todos: this.state.todos.filter(item => !item.done)
})
}
toggleAllSelect = (state) => {
this.setState({
todos: this.state.todos.map(item => {
return { ...item, done: state }
})
})
}
render() {
const { todos } = this.state
const length = todos.length
const doneLength = todos.reduce((total, item) => item.done ? total + 1 : total, 0)
return (
<div className="todo-container">
<div className="todo-wrap">
<Header addTodoObj={this.addTodoObj} />
<List todos={todos} updateTodos={this.updateTodos} deleteTodos={this.deleteTodos} />
<Footer length={length} doneLength={doneLength} deleteAllDoneTodo={this.deleteAllDoneTodo} toggleAllSelect={this.toggleAllSelect} />
</div>
</div>
)
}
}
- ES6 数组和对象的合并
- 事件定义时会受到参数,但绑定时无(),故不用 函数柯里化!!!(这是我当时犯的一个低级错误)。
Header
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { nanoid } from 'nanoid'
import './index.css'
export default class Header extends Component {
static propTypes = {
addTodoObj: PropTypes.func.isRequired,
}
handleKeyUp = (event) => {
const x = event.which || event.keyCode;
if (x !== 13) return
if (event.target.value.trim() === '') {
alert('输入不能为空')
return
}
const newObj = {
id: nanoid(),
name: event.target.value,
done: false
}
this.props.addTodoObj(newObj)
event.target.value = ''
}
render() {
return (
<div className="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.handleKeyUp} />
</div>
)
}
}
-
keyCode兼容性 在 Firefox 中, keyCode 属性在 onkeypress 事件中是无效的 (返回 0)。 浏览器兼容问题,可以一起使用 which 和 keyCode 属性来解决: -
易忘点:判断输入值不能为空
List
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class List extends Component {
static propTypes = {
todos: PropTypes.array.isRequired,
updateTodos: PropTypes.func.isRequired,
deleteTodos: PropTypes.func.isRequired
}
render() {
const { todos, updateTodos, deleteTodos } = this.props
return (
<ul className="todo-main">
{
todos.map(item => {
return <Item {...item} key={item.id} updateTodos={updateTodos} deleteTodos={deleteTodos} />
})
}
</ul>
)
}
}
- 不要忘了绑定key值
- 事件绑定时时render内部变量,不要同this
Item
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = { mouseState: false }
handlerMouseEvent = (flag) => {
return () => {
this.setState({ mouseState: flag })
}
}
updateTodos = (id) => {
return () => {
this.props.updateTodos(id)
}
}
deleteTodo = (id) => {
return () => {
if (window.confirm('确定删除吗?')) {
this.props.deleteTodos(id)
}
}
}
render() {
const { id, name, done } = this.props
const { mouseState } = this.state
return (
<li style={{ background: mouseState ? '#ddd' : '#fff' }} onMouseEnter={this.handlerMouseEvent(true)} onMouseLeave={this.handlerMouseEvent(false)} >
<label>
<input type="checkbox" checked={done} onChange={this.updateTodos(id)} />
<span>{name}</span>
</label>
<button className="btn btn-danger" onClick={this.deleteTodo(id)} style={{ display: mouseState ? 'block' : "none" }}>删除</button>
</li>
)
}
}
- 易忘点:删除时的弹框确认
- 函数柯里化
- 鼠标的移入移出,通过一个状态值来确认
Footer
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './index.css'
export default class Footer extends Component {
static propTypes = {
doneLength: PropTypes.number.isRequired,
length: PropTypes.number.isRequired,
toggleAllSelect: PropTypes.func.isRequired,
deleteAllDoneTodo: PropTypes.func.isRequired
}
render() {
const { length, doneLength, toggleAllSelect, deleteAllDoneTodo } = this.props
return (
<div className="todo-footer">
<label>
<input type="checkbox" checked={length === doneLength && length > 0} onChange={event => toggleAllSelect(event.target.checked)} />
</label>
<span>
<span>已完成{doneLength}</span> / 全部{length}
</span>
<button className="btn btn-danger" onClick={deleteAllDoneTodo}>清除已完成任务</button>
</div>
)
}
}
- 最后都将defaultChecked转化为checked,否则数据改变了但界面无变化
|