前面是类组件,今天是函数式组件
函数式组件的特点
没有状态
没有生命周期
没有this
useState
src>index.js
import React from 'react'
import { createRoot } from 'react-dom/client'
import './css/common.css' //引入公共样式
import App1 from './App1' //引入模块
const root = createRoot(document.getElementById('root'))
root.render(
<div>
<App1/>
</div>
)
src>css>common.css 公共样式文件
*{
padding:0;
margin:0;
list-style:none;
}
src>App1.js
import React, { useState } from 'react'
function App1() {
//useState(0)中的0是num的初始值,后面修改num的值需要通过setNum
const [num, setNum] = useState(0)
return (
<div>
<h3>num: {num}</h3>
</div>
)
}
*注意,Hooks的API只能在函数内的最外层声明
if(true){
const [num, setNum] = useState(0)
}
//报错,不能写在条件判断语句中
useEffect
将App1改为
import React, { useState, useEffect } from 'react'
function App1() {
// useState(0) 中的0是num的初始值
const [num, setNum] = useState(0)
const [num1, setNum1] = useState(0)
// 检测视图更新
useEffect(() => {
console.log('useEffect,视图更新了');
},[num])
//1 相当于componentDidMount:
// 第一次挂载一定会打印一次
//2 相当于componentDidUpdate:
// 回调的数组不写,默认所有数据更新都会打印
// 回调的数组写了对应的变量,则变量改变才打印
// 回调的数组为空,则·不检测更新
return (
<div>App1
<h3>num: {num}</h3>
<button onClick={() => setNum(num + 1)}>累加</button>
<h3>num1: {num1}</h3>
<button onClick={() => setNum1(num1 + 1)}>累加</button>
</div>
)
}
export default App1
useReducer 和useRef
类似于redux
src>App2.js:
import React, { useRef, useReducer } from 'react'
import './css/app2.css'
// 1、定义一个reducer
function texReducer(state, action) {
let newState = JSON.parse(JSON.stringify(state))
switch (action.type) {
case 'change_fn':
newState.texVal = action.value
return newState
case 'publish_fn':
if (action.value) {
newState.list.unshift(action.value)
newState.texVal = '' //数据初始化
} else {
alert('请输入非空的内容')
}
return newState
case 'del_fn':
newState.list.splice(action.value, 1)
return newState
default:
break
}
}
function App2() {
// 2、使用reducer并解构出state与dispatch, { texVal: '写点什么', list: [] }表示state的默认数据
const [state, dispatch] = useReducer(texReducer, { texVal: '写点什么', list: [] })
// 3.useRef获取元素
const element = useRef(null)
const changeFn = () => {
// 4.current指向ref挂载的dom元素
console.log(element.current.value);
// 5.dispatch调用
dispatch({ type: 'change_fn', value: element.current.value })
}
const publishFn =() => { dispatch({ type: 'publish_fn', value: state.texVal }) }
return (
<div className="box">
<div className='txtbox'>
<div className='title'>有什么新鲜事想告诉大家?</div>
<textarea name="" id="txta" ref={element} cols="30" rows="10" placeholder='请在此输入内容' value={state.texVal}
onChange={changeFn}>
</textarea>
<div className='foot'>
<div></div>
<div className='btn' onClick={publishFn}>发布</div>
</div>
</div>
<ul className="content">
{
state.list.map((item, index) => {
return <li key={index} className='foot'><div className='li-con'>{item}</div> <div className='btn' onClick={() => { dispatch({ type: 'del_fn', value: index }) }}>删除</div></li>
})
}
</ul>
</div>
)
}
export default App2
记得在index.js引入组件App2
src>css>app2.css 样式文件
html {
background-color: #eee;
}
.title {
color: #333;
height: 50px;
line-height: 50px;
}
.box {
width: 100%;
height: 100%;
}
.txtbox {
background-color: #fff;
width: 800px;
padding: 10px;
margin: 100px auto 10px;
}
#txta {
width: 800px;
height: 100px;
box-sizing: border-box;
border: 1px solid #ccc;
outline-color: #ff8140;
padding: 8px;
}
.content {
width: 800px;
padding: 10px;
margin: 0 auto;
background-color: #fff;
}
.foot {
display: flex;
justify-content: space-between;
height: 40px;
}
.btn {
width: 10%;
text-align: center;
height: 28px;
line-height: 28px;
margin: 6px 0;
background-color: #ff8140;
border-radius: 4px;
color: #fff;
}
ul li {
height: 50px;
line-height: 50px;
border-bottom: 1px solid #999;
}
.li-con {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 90%;
}