react创建项目&&常见的三大Hook
react创建项目&&常见的三大Hook
创建react脚手架项目
全局安装 create-react-app 工具:
npm i -g create-react-app
查看安装工具的版本号,注意V大写
create-react-app -V
进入要创建的文件目录创建react项目,名为:react_project
create-react-app react_project
启动项目会默认3000端口号打开浏览器
npm start
目录结构
node_modules/: 存放项目依赖包的目录。该目录会在运行 npm install 后生成
public/: 存放公共静态资源文件的目录
favicon.ico: 浏览器标签上的图标。
index.html: 主页面。
logo192.png
log512.png:logo图
manifest.json: 应用加壳的配置文件,定义了应用的图标、启动配置、显示模式等。
robots.txt:爬虫协议文件,控制哪些内容可以被抓取和索引,优化搜索引擎的表现,减少服务器负担,并保护网站的敏感数据。
src/:源码文件夹
App.css: App 组件的样式文件。
App.js: 主组件文件,包含应用的主要内容。
App.test.js: 对 App 组件的测试文件。
index.css: 全局样式文件。
index.js: 应用的入口文件,将 App 组件渲染到 HTML 文件中。
logo.svg: 默认的 React Logo 图像文件。
reportWebVitals.js: 用于记录和报告 Web Vitals 性能指标的文件。
.gitignore: Git 忽略文件列表,指定哪些文件和目录不应该被版本控制系统跟踪。
package.json: 项目配置文件,包含项目的依赖、脚本和其他元数据。
README.md: 项目的说明文件,通常包含项目的介绍、安装和使用说明。
package-lock.json: 自动生成的文件,锁定依赖的版本,以确保项目在不同的环境中安装一致的版本。
React Hooks
hooks是什么?
是react 16.8 引入的功能,允许在函数组件中使用state状态和其他react特性,而无需编写类组件
三个常用的Hook
- React.useState()
- React.useEffect()
- React.useRef()
useState Hook
用于在函数组件中添加状态,返回一个状态变量和一个函数,用于更新这个状态变量
eg(Components/index.jsx):
类式组件写法:
import React from 'react'
class Demo extends React.Component {
state = {count: 0}
add = ()=>{
this.setState(state => ({count:state.count+1}))
}
render () {
return (
<div>
<h1>和为{this.state.count}</h1>
<button onClick={this.add}>点我加1</button>
</div>
)
}
}
函数式组件写法:
import React,{useState} from 'react'
function Demo(){
const [count,setCount] = useState(0)
const [sex,setSex] = useState('女')
const [name,setName] = useState('小白')
function add(){
// console.log('点击');
// setCount(count+1) //第一种写法
setCount(count => count+1) //第二种
}
function changeSex(){
setSex(sex => (sex === '男'? '女': '男'))
}
function changeName(){
setName(name => name="小黑")
}
return (
<div>
<h1>和为:{count}</h1>
<h2>我是:{name}</h2>
<h2>性别:{sex}</h2>
<button onClick={add}>点我加1</button>
<button onClick={changeName}>点我改姓名</button>
<button onClick={changeSex}>点我改性别</button>
</div>
)
}
export default Demo
上述代码中useState(initialState):
- useState是一个函数,他接受一个初始状态initialState作为参数
- 返回一个数组,数组第一个元素是当前的状态值,第二个元素是一个函数,用于更新状态
setXXX()两种写法:
- setXXX(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值
- setXXX(value => newValue):参数为函数,接收原来的状态值,返回新的状态值,内部用其覆盖原来的状态值
useEffect Hook
用于处理副作用操作(数据获取、订阅、手动操作DOM等)。允许在函数组件中执行这些副作用操作,而不需要使用类组件的生命周期。
- 类式组件写法:
import React from 'react'
import ReactDOM from 'react-dom'
import root from '../../index'
// 类式组件
class Demo extends React.Component {
state = {count: 0}
add = ()=>{
this.setState(state => ({count:state.count+1}))
}
unmount = ()=>{
if(root){
root.unmount()
}
}
componentDidMount(){
// console.log('组件挂载完成')
this.timer = setInterval(()=>{
this.setState(state => ({count:state.count+1}))
},1000)
}
componentWillUnmount(){
// console.log('组件将要卸载')
clearInterval(this.timer)
}
render () {
return (
<div>
<h1>和为{this.state.count}</h1>
<button onClick={this.add}>点我加1</button>
<button onClick={this.unmount}>卸载</button>
</div>
)
}
}
export default Demo
- 函数式组件写法:
import React,{useEffect,useState} from 'react'
import ReactDOM from 'react-dom'
import root from '../../index'
function Demo(){
const [count,setCount] = useState(0)
// useEffect(()=>{
// console.log('-----');
// },[count])
/*
注意:上面的空数组,表示谁也不监测。
不写的话就是全都监测(组件挂载和更新都会监测到)
具体监测到什么就写什么,比如[count] 就只监测count
*/
useEffect(()=>{
let timer = setInterval(()=>{
setCount(count => count+1)
},3000)
return ()=>{
clearInterval(timer)
}
},[])
function add(){
setCount(count => count+1)
}
function onmount(){
if(root){
root.unmount();
}
}
return (
<div>
<h1>和为:{count}</h1>
<button onClick={add}>点我加1</button>
<button onClick={onmount}>卸载</button>
</div>
)
}
export default Demo
useRef Hook
类式组件写法:
import React from 'react'
import root from '../../index'
// 类式组件
class Demo extends React.Component {
state = {count: 0}
myRef = React.createRef()
add = ()=>{
this.setState(state => ({count:state.count+1}))
}
show = ()=>{
alert(this.myRef.current.value)
}
unmount = ()=>{
if(root){
root.unmount()
}
}
componentDidMount(){
// console.log('组件挂载完成')
this.timer = setInterval(()=>{
this.setState(state => ({count:state.count+1}))
},1000)
}
componentWillUnmount(){
// console.log('组件将要卸载')
clearInterval(this.timer)
}
render () {
return (
<div>
<h1>和为{this.state.count}</h1>
<input type="text" ref= {this.myRef} />
<button onClick={this.add}>点我加1</button>
<button onClick={this.unmount}>卸载</button>
<button onClick={this.show}>弹窗显示数据</button>
</div>
)
}
}
函数式组件写法:
import React,{useState,useEffect,useRef} from 'react'
import root from '../../index'
function Demo(){
const [count,setCount] = useState(0)
const myRef = useRef()
useEffect(()=>{
let timer = setInterval(()=>{
setCount(count => count+1)
},3000)
return ()=>{
clearInterval(timer)
}
},[])
function add(){
setCount(count => count+1)
}
function onmount(){
if(root){
root.unmount();
}
}
function show (){
alert(myRef.current.value)
}
return (
<div>
<h1>和为:{count}</h1>
<input type="text" ref = {myRef} />
<button onClick={add}>点我加1</button>
<button onClick={onmount}>卸载</button>
<button onClick={show}>点我显示输入框信息弹窗</button>
</div>
)
}
export default Demo
遇到的问题:
问题1:useEffect生命周期函数为什么会被进行两次调用?
问题分析:
这部分代码,我看到控制台--------
打印了两次,其原因是react的严格模式会在开发环境下对某些生命周期函数(包括useEffect)进行两次调用,这是为了验证副作用的影响,生产模式不会发生这种情况
解决方法:
在indedx.js入口文件中,删除或者注释掉React.StrictMode
即可
问题2:react组件卸载报错问题,如下:
报错显示及分析:
- unmountComponentAtNode is deprecated and will be removed in the next major release. 报错提示是因为unmountComponentAtNode已经被弃用
- You are calling ReactDOM.unmountComponentAtNode() on a container that was previously passed to ReactDOMClient.createRoot(). This is not supported. Did you mean to call root.unmount()?尝试用旧的卸载方法
ReactDOM.unmountComponentAtNode()
去卸载一个之前使用新的ReactDOM.createRoot()
方法创建的 React 根节点。这两者是不兼容的。 - unmountComponentAtNode(): The node you're attempting to unmount was rendered by React and is not a top-level container. Instead, have the parent component update its state and rerender in order to remove this component.你试图卸载的节点不是一个顶层容器节点。换句话说,你可能在试图卸载由 React 管理的子组件,而不是根节点。
解决方法:
通过将root导出并在其他地方导入来管理卸载组件,这是因为直接管理root对象是因为它提供了对react根结点更好的控制,尤其是在react18及以上版本时。他确保能正确地卸载和管理组件,而不会引发错误或与旧版本产生冲突
操作步骤:
- 在入口文件index.js导出root:
export default root;
- 在要卸载的组件文件中导入root:
import root from '../../index' //根据自己路径调整
function onmount(){
if(root){
root.unmount();
}
}
<button onClick={onmount}>卸载</button>