快速入门React
react
是一个用于构建用户界面
的前端框架,具有很丰富的生态工具
,包括ReactRouter AntDesign Redux Next
项目创建
create-react-app
create-react-app
是一个用于快速构建 react app 的脚手架,底层是基于webpack构建!
创建一个
react app
项目
npx create-react-app my-app
在
自定义
好一个项目目录文件夹
内,通过bash窗口
来执行npx命令
来创建一个react app项目
!
cd my-app
npm start
进入
项目内部
,执行npm start
即可启动项目
!
项目目录
创建
完项目
后,除了index.js
和App.js
以外多余的删掉
即可,记得把引用的静态资源
文件路径在App.js
中删除掉
,避免无法找到该文件报错
!
├── package-lock.json
├── package.json
├── public # 静态文件
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
├── README.md
└── src # 源代码文件
├── App.js
└── index.js # 入口文件
index.js
index.js
是入口文件
,react
和react-dom
是核心包!
ReactDom
获取publc -> index.html
中id
为root
的根元素
,并将App.js
内容挂载到根节点
!
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
App.js
function App() {
return (
<div className="App">
<center>
<h1>Welcome to React!</h1>
</center>
</div>
);
}
export default App;
基础内容
jsx
基础
jsx
是xml
和html
的一个缩写方式,可以通过标签
的写法,在其内部可通过{ }
大括号来访问js
中提供的变量信息
,是React UI
编写模版
的一种方式
!
const message = "Welcome to React!";
function App() {
return (
<div className="App">
<center>
<h1>{message}</h1>
</center>
</div>
);
}
jsx
语法本身在浏览器端
是不被识别
的,需要通过babel工具
,将jsx
转换为浏览器能够认识的代码
!
识别js
表达式
在
jsx
中使用表达式
,只需要在{ 表达式 }
花括号中使用即可,类似于模版字符串${ }
,可识别字符串、js变量、函数调用返回值、以及对象方法的调用
!
const message = "Welcome to React!";
function test(){
return 'test text';
}
function App() {
return (
<div className="App">
<center>
<h1>{"Hello, World!"}</h1>
<h1>{message}</h1>
<h1>{test()}</h1>
<h1>{new Date().toLocaleString()}</h1>
<h1>{Math.random()}</h1>
<h1>{[1, 2, 3, 4, 5].map(num => <p key={num}>{num}</p>)}</h1>
<h1 style={{color: 'blue'}}>测试样式</h1>
</center>
</div>
);
}
列表渲染
列表渲染,
key属性
是必要
的,必须是唯一值
!
const list = [
{id: 1, name: 'apple'},
{id: 2, name: 'banana'},
{id: 3, name: 'orange'},
{id: 4, name: 'grape'},
]
<ul>
{list.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
条件渲染
{flag && <h1>Flag is true</h1>}
{!flag && <h1>Flag is false</h1>}
{flag ? <h1>Flag is false</h1> : <h2>Flag is true</h2>}
事件绑定
function handleClick(){
console.log('clicked');
}
<button onClick={handleClick}>Click me</button>
参数绑定
function handleClick(name, e){
console.log('clicked');
}
<button onClick={(e) => handleClick("name", e)}>Click me</button>
React
函数组件
组件
,一般用来将重复的视图
部分,提取
出一个公共的组件
,供其他视图模块
使用,在React
中,定义一个组件
都可视为一个函数(Function)
且函数名首字母需要大写
!
定义一个
button
组件,components -> button -> index.js
function Button(props) {
return <button className="btn">{props.text}</button>;
}
export default Button;
在其它组件中使用
import Button from "./components/button";
const message = "Welcome to React!";
let flag = true;
function test(){
return 'test text';
}
const list = [
{id: 1, name: 'apple'},
{id: 2, name: 'banana'},
{id: 3, name: 'orange'},
{id: 4, name: 'grape'},
]
function handleClick(){
console.log('clicked');
}
function App() {
return (
<div className="App">
<center>
<h1>{"Hello, World!"}</h1>
<h1>{message}</h1>
<h1>{test()}</h1>
<h1>{new Date().toLocaleString()}</h1>
<h1>{Math.random()}</h1>
<h1>
{[1, 2, 3, 4, 5].map((num) => (
<p key={num}>{num}</p>
))}
</h1>
<h1 style={{ color: "blue" }}>测试样式</h1>
<ul>
{list.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
{flag && <h1>Flag is true</h1>}
{!flag && <h1>Flag is false</h1>}
{flag ? <h1>Flag is false</h1> : <h2>Flag is true</h2>}
<button onClick={(e) => handleClick("name", e)}>Click me</button>
<div className="">
自定义组件
<Button text="Hello, World!" />
</div>
</center>
</div>
);
}
export default App;
useState
状态变量
useState
在React
中是一个状态变量
,主要用于更新视图
,可理解为响应式
,当数据发生变化
时,通过useState函数来更新视图
!类似于
vue3
中的ref
和reactive
将变量
转换为响应式
!
// const [count, setCount] = useState(0); 不能在这定义 useState ❎
function App() {
const [count, setCount] = useState(0); // 必须在函数组件内部定义 useState ✅
return (
<div className="App">
<center>
<div className="custom-component">
{count + 1}
<button onClick={() => setCount(count + 1)}>useState 状态变量</button>
</div>
</center>
</div>
);
}
useState
可接受一个初始值的参数
,返回一个数组
,其中第一个是状态变量
,第二个是更新状态变量
的函数,当数据需要改变时
,调用setCount函数更新视图
!
需要注意的是,
useState Hooks函数
必须在组件函数内部使用
!
修改复杂类型
修改
引用类型
时,需要始终返回一个新的对象地址
修改才行,避免修改时影响对源对象引用地址
!
import { useState } from "react";
function App() {
const [ user, setUser ] = useState({ name: "Miao", age: 25 });
function modifyUser(){
// 参数传递一个新的对象,并对原有的属性进行覆盖,而不是在原有对象的地址上进行修改
setUser({...user, age: user.age + 1});
}
return (
<div className="App">
<center>
<div className="custom-component">
<p>{user.name} {user.age}</p>
<button onClick={modifyUser}>useState 修改对象</button>
</div>
</center>
</div>
);
}
export default App;
组件样式控制
使用
行内样式
行内样式,
多单词
之间使用驼峰命名
!<ul style={{ listStyle: "none", fontSize: "20px", color: "red" }}> {list.map((item) => ( <li key={item.id}>{item.name}</li> ))} </ul>
使用类样式
className
.app{ border: 1px solid red; }
import './index.css' fucntion App(){ return ( <div className = "app"> 类样式 </div> ) }
todoListDemo
index.css
.list{
border: 1px solid #ccc;
}
.list .item{
padding: 10px;
}
.list .item:not(:last-child){
border-bottom: 1px solid #ccc;
}
import { useState } from "react";
import "./styles/index.css"
const list = [
{ id: 1, name: "apple" },
{ id: 2, name: "banana" },
{ id: 3, name: "orange" },
{ id: 4, name: "grape" },
];
function App() {
const [items, setItems] = useState(list);
return (
<div className="App">
<center>
<h1>{"Hello, World!"}</h1>
<button onClick={() => setItems([...items, { id: items.length + 1, name: "new item" }])}>新增</button>
<div className="list">
{items.map((item) => (
<div key={item.id} className="item">
<h2>{item.name}</h2>
<div className="btn" style={{textAlign: "right"}}>
<button onClick={() => setItems(items.filter((i) => i.id !== item.id))}>
Delete
</button>
</div>
</div>
))}
</div>
</center>
</div>
);
}
export default App;
classname
动态类名优化
在
React
中,避免不了有些类名是需要条件切换的,通常会根据某一个条件来判断当前类名是否显示与否,比如,当前激活元素active设置为高亮,通常会通过${ currentIndex === index && 'active' }
,如果动态类名多了,看起来就比较费劲,这时需要classname
这个第三方插件,来改写下方式,减少复杂度!
安装插件
npm install classname
插件用法
表单数据绑定
表单
数据绑定
,就是数据双向绑定
,当输入内容
时,将数据同步
到状态变量
中,当状态变量
发生变化
时,会同时修改input
中的value
值!
大概原理与
vue
中的v-model
语法糖一致,vue
中会通过v-bind
的方式,来绑定value值
,且通过input事件
将值$emit出去并修改!
const [value, setValue] = useState("");
<input value = {value} @change="(e) => setValue(e.target.value)" type="text" />
useRef
获取dom
在
React
中,获取dom
需要通过useRef Hooks函数
来生成一个ref引用
,对目标元素
进行一个ref绑定
!
import { useRef, useState } from "react";
function App() {
const appRef = useRef(null);
return (
<div className="App" ref={appRef}>
<center>
<button onClick={() => console.log(appRef.current.getBoundingClientRect())}>点击获取dom</button>
</center>
</div>
);
}
export default App;
通过
appRef.current
属性来获取dom
,操作dom
时需要等待dom完全渲染完成
后才能使用
!
组件传递
父子通信
通过
props
里实现父子组件
通信
function Button(props) {
return <button className="btn">{props.text}</button>;
}
function Parent() {
let text = "新增";
return <Button text={text} />;
}
子父通信
子传父
,在React
中,通过父组件向子组件传递函数
,且在子组件来当做参数来调用父组件函数
!
function Button(props) {
let childrenMsg = 'children msg!';
return <button className="btn" onClick={() => props.getMessage(childrenMsg)}>{props.text}</button>;
}
function Parent() {
let text = "新增";
function getMessage(msg){
}
return <Button text={text} onGetMessage={ getMessage } />;
}
兄弟通信
使用
状态提升
,可以完成兄弟之间通信
,状态提升
,就是父组件
,将子组件中的数据
,获取到,并通过父传子的方式
,到另一个子组件
,便可完成跨兄弟组件通信
!
import { useState } from "react";
function Children1(props) {
console.log("Children1 props", props);
return <div>children1 {props.childrenMsg}</div>;
}
function Children2(props) {
let childrenMsg = 'Children2'
return (
<div>
{childrenMsg}
<button
className="btn"
onClick={() => props.onGetChildren2Msg(childrenMsg)}
>
Children2
</button>
</div>
);
}
function Button(props) {
const [ msg, setMsg ] = useState('');
const getChildren2Msg = ( msg ) => {
setMsg(msg);
console.log('parent Button', msg);
}
return (
<div>
<Children1 childrenMsg={msg} />
<Children2 onGetChildren2Msg={getChildren2Msg} />
<button className="btn">{props.text}</button>
</div>
);
}
export default Button;
跨组件通信
跨组件通信使用
Context
机制实现跨层传递!
创建
createContext()
返回一个上下文在
根组件
中,使用Provider
来提供数据在
子组件
或孙子组件
中,通过useContext
来获取上下文提供的数据
!
import { createContext, use, useContext, useState } from "react";
// 创建 createContext 上下文
const MsgContext = createContext();
function Children1(props) {
console.log("Children1 props", props);
// 使用useContext获取上下文,获取 provider 提供的数据
const msg = useContext(MsgContext);
console.log("Children1 msg 跨层传递消息 useContext", msg);
return <div>children1 {props.childrenMsg}</div>;
}
function Children2(props) {
let childrenMsg = 'Children2'
// props.getChildren2Msg(childrenMsg);
return (
<div>
{childrenMsg}
<button
className="btn"
onClick={() => props.onGetChildren2Msg(childrenMsg)}
>
Children2
</button>
</div>
);
}
function Button(props) {
const [ msg, setMsg ] = useState('');
const getChildren2Msg = ( msg ) => {
setMsg(msg);
console.log('parent Button', msg);
}
return (
<div>
<!-- 在根组件中使用 provider 向外提供数据-->
<MsgContext.Provider value={{ msg, setMsg }}>
<Children1 childrenMsg={msg} />
<Children2 onGetChildren2Msg={getChildren2Msg} />
</MsgContext.Provider>
<button className="btn">{props.text}</button>
</div>
);
}
export default Button;
useEffect
useEffect
主要用来处理副作用的,主要处理与渲染无关的逻辑,比如数据请求、dom操作、事件监听等相关逻辑!
useEffect
有两个参数,一个是回调函数
,另一个是依赖项数组
!useEffect(()=> {}, [])
useEffect
会在每次渲染完成后
执行调用,当设置第二个参数
为依赖空数组
时,则只会在第一次渲染结束后执行
!
当依赖项数组不为空时
,则会在依赖项发生变化
时,触发调用
!
依赖项: 就是页面中所展示依赖的数据项,页面数据发生变化时,触发操作!
import { useEffect, useState } from "react";
import "./styles/index.css"
function App() {
const [ counter, setCounter ] = useState(0);
// 不传入依赖项,useEffect在每次渲染时都会执行
/* useEffect(() => {
console.log("Hello, World!");
}); */
// 传入空数组,useEffect只会在第一次渲染时执行
/* useEffect(() => {
console.log("Hello, World!");
}, []); */
// 传入依赖项,useEffect只在counter变化时才会执行
useEffect(() => {
console.log("counter changed:", counter);
}, [counter]);
return (
<div className="App">
<center>
<h1>{("Hello, World!", counter)}</h1>
<button onClick={() => setCounter(counter + 1)}>+</button>
</center>
</div>
);
}
export default App;
useEffect 清除副作用
在
useEffect
内部可以返回一个函数
,在函数内部
可以实现组件卸载
后,执行一些逻辑操作,比如清除定时器
,以及移除事件
监听相关操作!
useEffect(() => {
console.log("counter changed:", counter);
const timer = setInterval(() => {
setCounter(counter + 1);
}, 1000);
return () => {
// 清除副作用
clearInterval(timer);
}
}, [counter]);
Hooks
Hooks
是函数封装
的一个概念,将复用逻辑提取
到函数
中进行处理后返回结果
!
Hooks
函数的定义需要以useXxx
的方式
去定义!
import { useState } from "react";
import "./styles/index.css"
function useToggle(){
const [ isOn, setIsOn ] = useState(false);
const toggle = () => setIsOn(!isOn);
return [ isOn, toggle ];
}
function App() {
const [ counter, setCounter ] = useState(0);
const [ isOn, toggle ] = useToggle();
return (
<div className="App">
<center>
<h1>{isOn ? "ON" : "OFF"}</h1>
<button onClick={toggle}>Toggle</button>
</center>
</div>
);
}
export default App;
Hooks 使用规范
Hooks
只能在组件函数内部
使用,且必须是顶层
,不能
包含在if
和for
以及其他函数模块中使用
!
- 不能在组件外部使用
import { useState } from "react"; import "./styles/index.css" function useToggle(){ const [ isOn, setIsOn ] = useState(false); const toggle = () => setIsOn(!isOn); return [ isOn, toggle ]; } const [ isOn, toggle ] = useToggle(); // no ❎ function App() { const [ counter, setCounter ] = useState(0); // const [ isOn, toggle ] = useToggle(); // yes ✅ return ( <div className="App"> <center> <h1>{isOn ? "ON" : "OFF"}</h1> <button onClick={toggle}>Toggle</button> </center> </div> ); }
- 不能在条件语句内使用
import { useState } from "react"; import "./styles/index.css" function useToggle(){ const [ isOn, setIsOn ] = useState(false); const toggle = () => setIsOn(!isOn); return [ isOn, toggle ]; } function App() { if( true ){ const [ counter, setCounter ] = useState(0); const [ isOn, toggle ] = useToggle(); // yes ❎ } return ( <div className="App"> <center> <h1>{isOn ? "ON" : "OFF"}</h1> <button onClick={toggle}>Toggle</button> </center> </div> ); }