创建项目
npx create-react-app project-name
基本语法
列表循环
function ListFor() {
const list = [
{ id: 1, name: "AAA" },
{ id: 2, name: "BBB" },
{ id: 3, name: "CCC" },
{ id: 4, name: "DDD" },
];
return (
<ul>
{list.map((item) => {
return <li key={item.id}>{item.name}</li>;
})}
</ul>
);
}
export default ListFor;
条件渲染
function IfView() {
const flag = true;
let content = null;
if (flag) {
content = <h1>Flag is true</h1>;
} else {
content = <h1>Flag is false</h1>;
}
return (
<>
{/* 第一种 三元 */}
{/* {flag ? <h1>Flag is true</h1> : <h1>Flag is false</h1>} */}
{/* 第二种 逻辑与 */}
{/* {flag && <h1>Flag is true</h1>} */}
{/* 第三种 if else */}
{content}
</>
);
}
export default IfView;
react基础事件绑定
function ClickBind() {
// 获取事件对象e
const handleClick = (e) => {
console.log("Button clicked");
console.log("获取事件对象参数", e);
};
// 传递参数
const handleParamClick = (param) => {
console.log("Button clicked with param", param);
};
// 同时传递事件对象和自定义参数
const handleEventAndParamClick = (param, e) => {
console.log("Button clicked with event and param", param, e);
};
const aaa = 100;
return (
<div>
<h1>ClickBind</h1>
<button onClick={handleClick}>Click me</button>
{/* 传递自定义参数 */}
{/* 注意:不能直接写函数调用,这里的事件绑定需要一个函数引用 */}
<button onClick={() => handleParamClick(aaa)}>传递自定义参数</button>
{/* 同时传递事件对象和自定义参数 */}
<button onClick={(e) => handleEventAndParamClick(aaa, e)}>
同时传递事件对象和自定义参数
</button>
</div>
);
}
export default ClickBind;
React组件
概念:一个组件就是用户界面的一部分,它可以有自己的逻辑和外观,组件之间可以互相嵌套,也可以复用多次。
在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和试图UI,渲染组件只需要把组件当成标签书写即可。
function Button(){
return <button>Click me! </button>
}
function App () {
return (
<div className="App">
<Button />
<Button></Button>
</div>
)
}
React 中的 useState
概念:useState 是一个 React Hook(函数),它允许我们向组件添加一个状态变量。从而控制影响组件的渲染结果。
本质:和普通JS变量不同的是,状态变量一旦发生变化,组件的视图UI也会跟着变化(数据驱动视图)
import { useState } from "react";
function TryUseState() {
// 调用 useState 添加一个状态变量
// count 是状态变量的名称,setCount 是更新状态变量的函数
const [count, setCount] = useState(0);
const [name, setName] = useState("John");
const increment = () => {
// 用传入的新值修改 count
setCount(count + 1);
};
return (
<div>
<h1>Try Use State</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<p>Name: {name}</p>
</div>
);
}
export default TryUseState;
修改状态的规则
在 React 中,状态被认为是只读的,我们应该始终替换它而不是修改它,直接i需改状态不能引发视图更新
import { useState } from "react";
function TryUseState() {
// 调用 useState 添加一个状态变量
// count 是状态变量的名称,setCount 是更新状态变量的函数
let [count, setCount] = useState(0);
const [name, setName] = useState("John");
const increment = () => {
// 直接修改,无法引起视图更新
count++;
console.log(count); // 打印出来是修改的
// 用传入的新值修改 count
// setCount(count + 1);
};
return (
<div>
<h1>Try Use State</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<p>Name: {name}</p>
</div>
);
}
export default TryUseState;
修改对象状态
import { useState } from "react";
function TryUseState() {
const [form, setForm] = useState({
name: "John",
age: 20,
});
const handleChange = () => {
setForm({
...form,
name: "Bob",
age: 30,
});
};
return (
<div>
<h1>Try Use State</h1>
<p>Name: {form.name}</p>
<p>Age: {form.age}</p>
<button onClick={handleChange}>ChangeForm</button>
</div>
);
}
export default TryUseState;
数组的修改
在 React 中你需要将数组视为只读的,不可以直接修改原数组。例如,不可以调用 arr.push(),arr.pop()等方法。也推荐使用 lodash库的方法
数组新增数据
import { useState } from "react"
function App() {
let [arr, setArr] = useState([1, 2, 3])
const heandleClick = () => {
setArr([...arr,4]) //末尾新增 扩展运算符
//setArr([0,...arr]) 头部新增 扩展运算符
}
return (
<>
<button onClick={heandleClick}>更改值</button>
<div id="aaa">{arr}</div>
</>
)
}
export default App
数组删除数据
import { useState } from "react"
function App() {
let [arr, setArr] = useState([1, 2, 3])
const heandleClick = () => {
setArr(arr.filter((item) => item !== 1)) //删除指定元素
}
return (
<>
<button onClick={heandleClick}>更改值</button>
<div id="aaa">{arr}</div>
</>
)
}
export default App
数组替换数据
import { useState } from "react"
function App() {
let [arr, setArr] = useState([1, 2, 3])
const heandleClick = () => {
setArr(arr.map(item => {
return item == 2 ? 666 : item
}))
}
return (
<>
<button onClick={heandleClick}>更改值</button>
<div id="aaa">{arr}</div>
</>
)
}
export default App
指定位置插入元素
import { useState } from "react"
function App() {
let [arr, setArr] = useState([1, 2, 3])
const heandleClick = () => {
let startIndex = 0
let endIndex = 2;
setArr(
[
...arr.slice(startIndex, endIndex),
2.5,
...arr.slice(endIndex)
]
)
}
return (
<>
<button onClick={heandleClick}>更改值</button>
<div id="aaa">{arr}</div>
</>
)
}
export default App
数组的排序旋转等
import { useState } from "react"
function App() {
let [arr, setArr] = useState([1, 2, 3])
const heandleClick = () => {
let newList = [...arr].map(v => v + 1) //拷贝到新数组
newList.sort((a, b) => b - a)
//newList.reverse()旋转
setArr(newList)
}
return (
<>
<button onClick={heandleClick}>更改值</button>
<div id="aaa">{arr}</div>
</>
)
}
export default App
组件样式处理
行内样式(不推荐)
function StyleClass() {
return (
<div
style={{
backgroundColor: "red",
width: "100px",
height: "100px",
}}
></div>
);
}
export default StyleClass;
class类名控制
.box{
width: 100px;
height: 100px;
background-color: red;
}
// 导入样式文件
import "./index.css";
function StyleClass() {
return (
// <div
// style={{
// backgroundColor: "red",
// width: "100px",
// height: "100px",
// }}
// ></div>
<div className="box">Hello</div>
);
}
export default StyleClass;
动态样式绑定
import { useState } from "react";
import "./index.css";
function StyleClass() {
const [isRed, setIsRed] = useState(false);
const changeColor = () => {
setIsRed(!isRed);
};
return (
<>
<div>
<div className={`box ${isRed ? "box-active" : ""}`}>Hello</div>
<button onClick={changeColor}>切换样式</button>
</div>
</>
);
}
export default StyleClass;
classnames优化类名控制
classnames 是一个简单的 JS 库,可以非常方便的通过条件动态控制 class类名的显示
安装
npm i classnames
使用
import { useState } from "react";
import "./index.css";
import classNames from "classnames";
function StyleClass() {
const [isRed, setIsRed] = useState(false);
const changeColor = () => {
setIsRed(!isRed);
};
return (
<>
<div>
{/* <div className={`box ${isRed ? "box-active" : ""}`}>Hello</div> */}
<div className={classNames("box", { "box-active": isRed })}>Hello</div>
<button onClick={changeColor}>切换样式</button>
</div>
</>
);
}
export default StyleClass;
受控表单绑定
概念:使用 React 组件的状态(useState)控制表单的状态
import { useState } from "react";
function Form() {
const [inputValue, setInputValue] = useState("");
const handleInputChange = (e) => {
setInputValue(e.target.value);
console.log("当前输入框的值", inputValue);
};
return (
<>
<input
type="text"
value={inputValue}
onChange={(e) => handleInputChange(e)}
/>
<div>
<button>按钮</button>
</div>
</>
);
}
export default Form;
React 中的 useRef
在 React 中获取/操作 DOM,需要使用 useRef钩子函数,分为两步:
- 使用useRef 创建 ref 对象,并与 JSX 绑定
const inputRef = useRef(null)
<input type="text" ref={inputRef} />
- 在 DOM 可用时,通过 inputRef.current 拿到 DOM 对象
import { useRef } from "react";
function UseRef() {
const inputRef = useRef(null);
// dom 可用时,通过 ref.current 获取 input 元素
const showDom = () => {
console.log(inputRef.current);
};
return (
<>
<input type="text" ref={inputRef} />
<div>
<button onClick={showDom}>获取DOM</button>
</div>
</>
);
}
export default UseRef;
组件通信
概念:组件通信就是组件之间的数据传递,根据组件嵌套关系的不同,有不同的通信方法
父子通信
父传子
注意:子组件只能读取 props中的数据,不能直接进行修改,父组件的数据只能由父组件修改。
import { useState } from "react";
import ChildOne from "./ChildOne";
import "./parent.css";
export default function Parent() {
const [count, setCount] = useState(0);
return (
<>
<div className="container">
<h1>我是父组件</h1>
<button onClick={() => setCount(count + 1)}>count增加1</button>
</div>
<hr />
<ChildOne count={count} />
</>
);
}
function ChildOne({ count }) {
return (
<div style={{ padding: "10px", backgroundColor: "#eee" }}>
<h1>我是ChileOne组件</h1>
<hr />
<h1>父组件传递过来的count值:{count}</h1>
</div>
);
}
export default ChildOne;
特殊的 prop children
场景:当我们把内容嵌套在子组件标签中时,父组件会自动在名为 children的 prop 属性中接收该内容。
<Son>
<span>This is a span</span>
</Son>
props:
children:<span />
子传父
核心:在子组件中调用父组件中的函数并传递实参
import { useState } from "react";
import ChildOne from "./ChildOne";
import "./parent.css";
export default function Parent() {
const [count, setCount] = useState(0);
const receiveChild = (data) => {
console.log("父组件接收到的数据", data);
};
return (
<>
<div className="container">
<h1>我是父组件</h1>
<button onClick={() => setCount(count + 1)}>count增加1</button>
</div>
<hr />
<ChildOne count={count} onReceive={receiveChild} />
</>
);
}
import { useState } from "react";
function ChildOne({ count, onReceive }) {
const [name, setName] = useState("Child One");
return (
<div style={{ padding: "10px", backgroundColor: "#eee" }}>
<h1>我是ChileOne组件</h1>
<hr />
<h1>父组件传递过来的count值:{count}</h1>
<button onClick={() => onReceive(name)}>向父组件传递数据</button>
</div>
);
}
export default ChildOne;
兄弟通信
使用状态提升实现兄弟组件通信
实现思路:借助“状态提升”机制,通过父组件进行兄弟组件之间的数据传递
import { useState } from "react";
import ChildOne from "./ChildOne";
import ChildTwo from "./ChildTwo";
import "./parent.css";
export default function Parent() {
const [count, setCount] = useState(0);
const receiveChild = (num) => {
console.log("父组件接收到的数据", num);
setCount(num);
};
return (
<>
<div className="container">
<h1>我是父组件</h1>
<p>ChildOne传递的num值:{count}</p>
</div>
<hr />
<ChildOne onReceive={receiveChild} />
<hr />
<ChildTwo count={count} />
</>
);
}
import { useState } from "react";
function ChildOne({ onReceive }) {
const [num, setNum] = useState(100);
const sendNum = () => {
setNum(num + 1);
onReceive(num);
};
return (
<div style={{ padding: "10px", backgroundColor: "#eee" }}>
<h1>我是ChileOne组件</h1>
<hr />
<button onClick={() => sendNum()}>向父组件传递数据</button>
</div>
);
}
export default ChildOne;
function ChildTwo({ count }) {
return (
<div style={{ backgroundColor: "pink", padding: "10px" }}>
<h1>我是Child Two组件</h1>
<p>This is the second child component</p>
<p>接收到的从 ChildOne 组件传递的 Count: {count}</p>
</div>
);
}
export default ChildTwo;
跨层通信
使用 Context机制跨层级组件通信
实现步骤:
使用 CreateContext方法创建一个上下文对象 Ctx
在顶层组件中(Top)通过 Ctx.Provider 组件提供数据
在底层组件(Bottom)中通过 useContext钩子函数获取消费数据
示例:跨文件使用 CreateContext
import { useState } from "react";
import Middle from "./Middle";
import { MyContext } from "./pass";
function Top() {
const [message, setMessage] = useState("Hello from Top");
const handleClick = () => {
setMessage("Top Message Changed");
};
return (
<>
<MyContext.Provider value={message}>
This is Top component.
<hr />
<button onClick={() => handleClick()}>修改数据观察是否传递</button>
<hr />
<Middle />
</MyContext.Provider>
</>
);
}
export default Top;
import Bottom from "./Bottom";
function Middle() {
return (
<div style={{ border: "1px solid black" }}>
<h1> This is Middle component.</h1>
<hr />
<Bottom />
</div>
);
}
export default Middle;
import { useContext } from "react";
import { MyContext } from "./pass";
function Bottom() {
const message = useContext(MyContext);
return (
<>
<h1>我是底层组件</h1>
<hr />
<h1>接收到的数据是:{message}</h1>
</>
);
}
export default Bottom;
定义的中间文件
import { createContext } from "react";
// 1. 使用 CreateContext方法创建一个上下文对象 Ctx
const MyContext = createContext();
// 将其导出供顶层组件及底层组件使用
export { MyContext };
React 中的 useEffect
useEffect是一个 React Hook 函数,用于在 React 组件中创建不是由事件引起而是由渲染本身引起的操作,比如发送 AJAX 请求,更改 DOM 等。
基本使用
语法
useEffect(() => { },[])
参数1是一个函数,可以将其称为副作用函数,在函数内部可以放置需要执行的操作
参数2是一个数组(可选参数),在数组里放置依赖项,不同的依赖项会影响第一个参数函数的执行,当是一个空数组的时候,副作用函数只会在组件渲染完毕之后执行一次
需求:在组件渲染完毕之后,立刻从服务端获取到列表数据并显示在页面中
import { useEffect, useState } from "react";
const URL = "http://geek.itheima.net/v1_0/channels";
export default function TryUseEffect() {
const [channels, setChannels] = useState([]);
// const [loading, setLoading] = useState(true);
useEffect(() => {
const getChannels = async () => {
const res = await fetch(URL);
const list = await res.json();
console.log(222, list.data.channels);
setChannels(list.data.channels);
};
getChannels();
}, []);
return (
<>
<h1>This is TryUseEffect Component!</h1>
<div>下面是频道列表</div>
<hr />
<ul>
{channels.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</>
);
}
useEffect依赖项参数说明
useEffect副作用函数的执行时机存在多种情况,根据传入依赖项的不同,会有不同的执行表现。
没有依赖项
在组件初始渲染和组件更新时执行
import { useEffect, useState } from "react";
export default function TryUseEffect() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("useEffect 执行啦!");
});
return (
<>
<h1>This is TryUseEffect Component! {count} </h1>
<button onClick={() => setCount(count + 1)}>修改 count 的值</button>
</>
);
}
// 执行情况
// 组件渲染时执行了一次,每次修改 count 的值,副作用函数也都会执行
空数组依赖项
只在初始渲染时执行一次
import { useEffect, useState } from "react";
export default function TryUseEffect() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("useEffect 执行啦!");
}, []);
return (
<>
<h1>This is TryUseEffect Component! {count} </h1>
<button onClick={() => setCount(count + 1)}>修改 count 的值</button>
</>
);
}
// 执行情况
// 只在组件初始渲染时执行了一次,后续 count 值变化时副作用函数也没有执行
特定依赖项
import { useEffect, useState } from "react";
export default function TryUseEffect() {
const [count, setCount] = useState(0);
const [num, setNum] = useState(2);
useEffect(() => {
console.log("useEffect 执行啦!");
}, [count]);
return (
<>
<h1>
This is TryUseEffect Component! Count: {count} Num: {num}
</h1>
<button onClick={() => setCount(count + 1)}>修改 count 的值</button>
<hr />
<button onClick={() => setNum(num * 2)}>修改 num 的值</button>
</>
);
}
// 执行情况
// 组件初始渲染时执行了一次,依赖项 count 变化时,副作用函数也会执行,而 num 变化时,副作用
// 函数并未执行
useEffect清除副作用
在 useEffect中编写由渲染本身引起的对接组件外部的操作,称作副作用操作,比如在 useEffect中开启了一个定时器,我们想在组件卸载时把定时器清理掉,这个过程就是清理副作用。
useEffect(() => {
// 实现副作用操作逻辑
return ()=>{
// 清除副作用逻辑
}
},[])
// 清除副作用的函数最常见的执行时机时在组件卸载时自动执行
实例
import { useState, useEffect } from "react";
function Son() {
// 渲染完毕后开启定时器,组件销毁时清除定时器
useEffect(() => {
const timer = setInterval(() => {
console.log("定时器开启");
}, 1000);
return () => {
clearInterval(timer);
console.log("定时器关闭");
};
});
return (
<div style={{ backgroundColor: "red", width: "100%", height: "200px" }}>
我是子组件
<hr />
</div>
);
}
function Father() {
const [flag, setFlag] = useState(true);
return (
<>
我是父组件
<p></p>
<button onClick={() => setFlag(!flag)}>切换</button>
<hr />
<p>下面是子组件</p>
{flag && <Son />}
</>
);
}
export default Father;
自定义Hook函数
基础使用
例:切换组件的显示与隐藏效果
import { useState } from "react";
function Custom() {
const [show, setShow] = useState(false);
return (
<div>
<button onClick={() => setShow(!show)}>Toggle</button>
{show && <p>This is a custom hook test</p>}
</div>
);
}
export default Custom;
// 问题:布尔值且切换部分的逻辑,当要使用切换的场景过多时,需要复制多份,不方便服用,当前组件耦合
// 度太高,这就需要自定义一个 Hook 方便复用
抽离切换逻辑,自定义Hook函数使用
import { useState } from "react";
export default function useToggle(initialState = false) {
// 创建一个状态变量和一个更新状态的函数
const [state, setState] = useState(initialState);
// 创建一个切换状态的函数
const toggle = () => setState(!state);
// 返回状态变量和切换状态的函数
return {
state,
toggle,
};
}
import useToggle from "./useToggle";
function Custom() {
const { state, toggle } = useToggle();
return (
<div>
<button onClick={toggle}>Toggle</button>
{state && <p>This is a custom hook test</p>}
</div>
);
}
export default Custom;
React Hooks使用规则
- 只能在组件中或者其他自定义 Hook 函数中调用
- 只能在组件的顶层调用,不能嵌套在 if、for 等其他函数中