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.pnglog512.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>