1 Create a react project using creat-react-app
Scaffolding is built with webpack, we don't have to build it from scratch now
Xxx scaffolding: used to help the program to quickly create a template project based on the xxx library
- Contains all required configurations (syntax check, jsx compilation, devServer...)
- Downloaded all related dependencies
- A simple effect can be run directly
React provides a scaffolding library for creating react projects: create-react-app (a project based on React scaffolding, read each file of the project, and then add your own business logic)
The overall technical architecture of the project is: react+webpack+es6+eslint
Features of projects developed using scaffolding: modularization, componentization, and engineering
With this library create-react-app
, scaffolding can be created
1.1 Create a project and start it
The old version of the steps to create a project
Global installation:npm i -g create-react-app
Switch to the directory where you want to create the project and use the command:create-react-arr hello-react
Go to the project folder:cd hello-react
Startup project:npm start
Now steps to create project
npx create-react-app my-app
cd my-app
npm start
1.2 react scaffolding project structure
public ---- static resource folder
favicon.icon ------ website tab icon
index.html -------- main page
logo192.png ------- logo image
logo512.png ------- logo image
manifest.json ----- Application packer configuration file
robots.txt -------- crawler protocol file
src ---- source code folder
App.css -------- App component style
App.js --------- App component
App.test.js ---- used to test the App
index.css ------ styles
index.js ------- entry file (equivalent to main.js)
logo.svg ------- logo image
reportWebVitals.js
— page performance analysis file (requires the support of web-vitals library)
setupTests.js
---- Component unit test file (requires jest-dom library support)
<!-- React.StrictMode帮我们检查App组件及其子组件里面的写法是否合理 -->
<React.StrictMode>
<App />
</React.StrictMode>
1.3 Hello component experience
a detail
第一种情况:
//文件module.js中定义
const React = {
a:1, b:2}
React.Component = class Component {
}
export default React
//使用
import React from './module.js'
const {
Component} = React//这叫从React身上解构
第二种情况:
const React = {
a:1, b:2}
export class Component {
}//使用分别暴露,暴露Component
export default React//使用默认暴露,暴露React
//使用
import React,{
Component} from './module.js'
//注意这不叫解构!!!这就是分别暴露之后的与引入,说明module文件里面使用了多种暴露形式
Distinguish between component and normal function files
The first type: when all js ends, look at the first letter, and the first letter of the component is capitalized
The second way: write the suffix of the component file as .jsx
, there are two kinds of suffixes that can be omitted when importing files in react: js and jsx
Optimization point: when importing files, the path name is too long, you can define index.jsx under the folder, so that you can omit the file name when importing, and write less
Entry file index.js
//React18不推荐这样写,会有警告,新写法可以参考React18
//引入react核心库
import React from "react";
//引入ReactDOM
import ReactDOM from "react-dom";
import App from './App'
//渲染App组件到页面
ReactDOM.render(<App />, document.getElementById('root'))
One note: Because subcomponents are eventually introduced and used in the App component, if subcomponent style naming conflicts will occur, it is sometimes necessary to do style modularization (less can be avoided by nesting relationships)
2 React plugin in VScode
ES7+ React/Redux/React-Native snippets 作者:dsznajder
3. Component coding process of functional interface
Split components: split interface, extract components
Realize static components: use components to achieve static page effects
Implement dynamic components
Dynamically display initialization data
data type
Data name
Which component is saved in?
Interaction (starting from binding event listening)
4 TodoList case
Split static components and styles
App.jsx puts subcomponents, App.css puts public styles, and the rest of the components take away
About where todos data is placed
At present, we have only learned how to pass data props from father to son, so the data is temporarily placed in the common father App
parent to child data transfer
//App给List组件传
<List todos={
this.state.todos} />
//List组件给Item组件传
//接收、解构
const {
todos } = this.props
//传递的两种写法
//return <Item key={todo.id} id={todo.id} name={todo.name} done={todo.done} />
return <Item key={
todo.id} {
...todo} />
//Item组件同样接收、解构、展示即可
Added todo: The input in the Header should cause the state change in the App component (transfer from the child to the parent)
The App component passes a function to the Header subcomponent. The function callback is written in the App, and the data in the subcomponent can be returned to the parent component App in the form of parameters.
App.jsx
//用于添加一个todo,接收一个todo对象
addTodo = (todoObj) => {
//获取原todos
const {
todos } = this.state
//在数组前方追加一个todo
const newTodos = [todoObj, ...todos]
//更新状态
this.setState({
todos: newTodos })
}
<Header addTodo={
this.addTodo} />
Header.jsx
//绑定回车事件
<input onKeyUp={
this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认" />
//绑定事件的元素和要操作的元素相同,可以不用ref
handleKeyUp = (event) => {
//回车键keycode是13,现在不推荐使用keyCode了,event.keyCode !== 13
if (event.key !== 'Enter') return
//添加的todo名字不能为空
if (event.target.value.trim() === '') {
alert('输入不能为空')
return
}
//准备好新增的todoObj
const todoObj = {
id: nanoid(), name: event.target.value, done: false }
//将todoObj传递给App(子给父)
this.props.addTodo(todoObj)
//清空输入框
event.target.value = ''
}
mouse move in and out effect
Item.jsx
state = {
mouse: false }
//鼠标移入移出的回调,高阶函数写法
handleMouse = (flag) => {
//注意:回调中函数可以写小括号,但是无论什么情况都要给事件一个回调函数,返回值是函数耶ok
return () => {
this.setState({
mouse: flag })
}
}
<li style={
{
backgroundColor: this.state.mouse ? '#95b196' : 'white' }} onMouseEnter={
this.handleMouse(true)} onMouseLeave={
this.handleMouse(false)}>
...
<button className="btn btn-danger" style={
{
display: this.state.mouse ? 'block' : 'none' }}>删除</button>
</li>
Todo is checked, the focus is on changing the state
Get the id and check status of the item to be operated in the Item subcomponent—notify the App to update the state data (sun to father----pass layer by layer)
App.jsx
//更新todo对象---接收Item传过来的id和勾选
updateTodo = (id, done) => {
//获取原状态的todos
const {
todos } = this.state
//匹配处理数据
const newTodos = todos.map((todoObj) => {
//解构,然后相同的覆盖
if (todoObj.id === id) return {
...todoObj, done: done }
else return todoObj
})
//更新状态
this.setState({
todos: newTodos })
}
//给子组件List传递一个函数
<List todos={
this.state.todos} updateTodo={
this.updateTodo} />
List.jsx
//自己不用,反手就交给自己的子组件Item
const {
todos, updateTodo } = this.props
return <Item key={
todo.id} {
...todo} updateTodo={
updateTodo} />
Item.jsx
//给勾选框绑定onChange事件,传入操作的id
<input type="checkbox" defaultChecked={
done} onChange={
this.handleCheck(id)} />
//勾选、取消勾选的回调
handleCheck = (id) => {
//给input绑定,拿input的值,不需要ref,借助event即可
//孙子组件给App传递数据
return (event) => {
this.props.updateTodo(id, event.target.checked)
}
}
Summary: Where the state is, there is the method of manipulating the state
Limit props delivery
App passes data to Header and List, so limit the received data in Header and List components
Header.jsx and List.jsx
//引入
import PropTypes from 'prop-types'
//对接收的props进行:类型、必要性限制
static propTypes = {
addTodo: PropTypes.func.isRequired
}
static propTypes = {
todos: PropTypes.array.isRequired,
updateTodo: PropTypes.func.isRequired
}
delete a todo
App.jsx
//删除一个todo
deleteTodo = (id) => {
//获取原状态的todos
const {
todos } = this.state
//删除指定id的todo对象
const newTodos = todos.filter((todoObj) => {
return todoObj.id !== id
})
//更新状态
this.setState({
todos: newTodos })
}
//准备好的函数给孙子组件传过去,一层一层传,借助子组件List
<List todos={
this.state.todos} updateTodo={
this.updateTodo} deleteTodo={
this.deleteTodo} />
List.jsx
//反手交给Item,可以先解构
return <Item key={
todo.id} {
...todo} updateTodo={
updateTodo} deleteTodo={
deleteTodo} />
Item.jsx
//绑定事件
<button onClick={
() => this.handleDelete(id)} className="btn btn-danger" style={
{
display: this.state.mouse ? 'block' : 'none' }}>删除</button>
//删除一个todo的回调,非高阶写法
handleDelete = (id) => {
if (window.confirm('确定删除吗?')) {
this.props.deleteTodo(id)
}
}
Bottom Footer
defaultChecked can only take effect at the first time, after modification there is no responsedefaultChecked={doneCount === total ? true : false}
defaultChecked is actually pretty tricky! ! !
checked will report an error and requires onChange
<input type="checkbox" onChange={this.handleCheckAll} checked={doneCount === total ? true : false} />
First complete the simple select all button
App.jsx
//全选
checkAll = (done) => {
//获取原状态的todos
const {
todos } = this.state
//加工数据
const newTodos = todos.map((todoObj) => {
return {
...todoObj, done: done }
})
//更新状态
this.setState({
todos: newTodos })
}
//传过去
<Footer todos={
this.state.todos} checkAll={
this.checkAll} />
Footer.jsx
//已完成个数--条件统计
const doneCount = todos.reduce((pre, todo) => {
return pre + (todo.done ? 1 : 0) }, 0)
//总数
const total = todos.length
//展示数据
<span>已完成{
doneCount}</span> / 全部{
total}
//写成defaultChecked会有bug(仅在第一次可以),写成checked会警告,所以写成回调形式
//全选checkbox的回调
handleCheckAll = (event) => {
this.props.checkAll(event.target.checked)
}
<input type="checkbox" onChange={
this.handleCheckAll} checked={
doneCount === total && total !== 0 ? true : false} />
Then write clear done button function
App.jsx
//清除已完成的
clearChecked = () => {
//获取原状态的todos
const {
todos } = this.state
const newTodos = todos.filter((todoObj) => {
return todoObj.done === false
})
//更新状态
this.setState({
todos: newTodos })
}
//传过去
<Footer todos={
this.state.todos} checkAll={
this.checkAll} clearChecked={
this.clearChecked} />
Footer.jsx
//清除已完成
handleClearChecked = () => {
this.props.clearChecked()
}
<button onClick={
this.handleClearChecked} className="btn btn-danger">清除已完成任务</button>
Fully functional demo