React 基础
配套资料
React 起源与发展
React 与传统 MVC 的关系
React 的特性
虚拟 DOM
为什么学习 React?
react 文档
配置开发环境
npm install -g create-react-app
create-react-app 项目名称
npx create-react-app 项目名称
npm 和 npx 的区别
页面的显示流程
create-react-app react-demo
npm run start
端口以自己的为准, 默认为 3000
NPM vs YARN
npm i或yarn
npm i {库名}或yarn add {库名}
npm i {库名} --save-dev或yarn add {库名} --dev
npm uninstall package --save或yarn remove package
npm update --save或yarn upgrade
npm install package -g或yarn global add package
编写第一个 react 应用程序
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));root.render(<h1>欢迎进入React的世界</h1>);
什么是 jsx
注意:JSX 并不是标准的 JS 语法,是 JS 的语法扩展,浏览器默认是不识别的,脚手架中内置的 @babel/plugin-transform-react-jsx 包,用来解析该语法 如下图, 左边使我们编写的声明式代码, 右边是解析后的命令式代码
JSX 中使用 js 表达式
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));root.render(<h1>欢迎进入React的世界</h1>);
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 定义一个用户名const username = "张三";root.render(<h1>你好, 我叫{username}</h1>);
特别注意: if 语句/ switch-case 语句/ 变量声明语句
,这些叫做语句,不是表达式,不能出现在 {}
中!!
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 定义一个用户名const username = "张三";// 定义一个函数, 用来计算年龄const getAge = () => { return 30;};root.render( <h1> 你好, 我叫{username} , 今年 {getAge()} 岁了!!! </h1>);
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 定义一个用户名const username = "张三";// 定义一个函数, 用来计算年龄const getAge = () => { return 30;};// 定义一个布尔值const flag = true;root.render( <h1> 你好, 我叫{username} , 今年 {getAge()} 岁了!!! {flag ? "已经结婚了" : "还没结婚"} </h1>);
JSX 列表渲染
const songs = [ { id: 1, name: "粉红高跟鞋" }, { id: 2, name: "伤心太平洋" }, { id: 3, name: "雨一直下" },];
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 定义一个歌单const songs = [ { id: 1, name: "粉红高跟鞋" }, { id: 2, name: "伤心太平洋" }, { id: 3, name: "雨一直下" },];root.render( <div> <h1>歌单</h1> <ul> {songs.map((song) => ( <li key={song.id}>{song.name}</li> ))} </ul> </div>);
<li key={song.id}>{song.name}</li>
- key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用
- key 在当前列表中要唯一的字符串或者数值(String/Number)
- 如果列表中有像 id 这种的唯一值,就用 id 来作为 key 值
- 如果列表中没有像 id 这种的唯一值,就可以使用 index(下标)来作为 key 值
JSX 条件渲染
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));const flag = true;root.render(<h1>{flag ? "好棒棒!!!" : "真菜..."}</h1>);
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));const type = 1;const getFlag = () => { if (type === 1) { return <h1>this is h1</h1>; } if (type === 2) { return <h2>this is h2</h2>; } if (type === 3) { return <h3>this is h3</h3>; }};root.render(<div>{getFlag()}</div>);
JSX 样式处理
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));const color = "red";const backgroundColor = "black";
root.render(<h1 style={{ color: color, backgroundColor: backgroundColor }}>this is h1</h1>);
root.render(<h1 style={{ color, backgroundColor }}>this is h1</h1>);
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));const color = "red";const backgroundColor = "black";const styleObj = { color, backgroundColor,};root.render(<h1 style={styleObj}>this is h1</h1>);
.title { color: olivedrab; font-size: 44px;}
import ReactDOM from "react-dom/client";// 导入cssimport "./app.css";const root = ReactDOM.createRoot(document.getElementById("root"));root.render(<h1 className="title">this is h1</h1>);
.title { color: orangered; font-size: 48px;}
import ReactDOM from "react-dom/client";import "./app.css";const showTitle = true;const root = ReactDOM.createRoot(document.getElementById("root"));root.render(<h1 className={showTitle ? "title" : ""}>hello world</h1>);
JSX 注意事项
import ReactDOM from "react-dom/client";import "./app.css";
const root = ReactDOM.createRoot(document.getElementById("root"));root.render(<h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1>);
import ReactDOM from "react-dom/client";import "./app.css";const root = ReactDOM.createRoot(document.getElementById("root"));root.render( <> <h1>hello world</h1> <h1>hello world</h1> <h1>hello world</h1> <h1>hello world</h1> </>);
import ReactDOM from "react-dom/client";import "./app.css";const root = ReactDOM.createRoot(document.getElementById("root"));root.render( // class属性换成className <form action="" className="myForm"> gender: <input type="radio" name="" id="myGender" /> {/* for属性换成htmlFor */} <label htmlFor="myGender">male</label> </form>);
vscode 插件
🚩jsx 练习
npm i或者yarn
npm run start或者yarn start
list: [ { id: 1, author: "刘德华", comment: "给我一杯忘情水", time: 1633828140000, // 1: 点赞 0:无态度 -1:踩 attitude: 1, }, { id: 2, author: "周杰伦", comment: "哎哟,不错哦", time: 1633914540000, // 1: 点赞 0:无态度 -1:踩 attitude: 0, }, { id: 3, author: "五月天", comment: "不打扰,是我的温柔", time: 1633918140000, // 1: 点赞 0:无态度 -1:踩 attitude: -1, }, ],
{ /* 评论列表 */}<div className="comment-list"> {state.list.map((item) => { return ( <div className="list-item" key={item.id}> <div className="user-face"> <img className="user-head" src={avatar} alt="" /> </div> <div className="comment"> <div className="user">尤雨溪</div> <p className="text">前排吃瓜</p> <div className="info"> <span className="time">2021-10-08 09:05:00</span> <span className="like liked"> <i className="icon" /> </span> <span className="hate hated"> <i className="icon" /> </span> <span className="reply btn-hover">删除</span> </div> </div> </div> ); })}</div>;
/** * 时间戳转日期 * @param {number} timestamp 毫秒级时间戳 * @returns {string} "2022-12-9 19:00:34" */function timestampToTime(timestamp) { var date = new Date(timestamp); var Y = date.getFullYear() + "-"; var M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "-"; var D = date.getDate() + " "; var h = (date.getHours() + "").padStart(2, "0") + ":"; // 两位, 前补零 var m = (date.getMinutes() + "").padStart(2, "0") + ":"; // 两位, 前补零 var s = (date.getSeconds() + "").padStart(2, "0"); // 两位, 前补零 return Y + M + D + h + m + s;}
<div className="info"> <span className="time">{timestampToTime(item.time)}</span> <span className={item.attitude === 1 ? "like liked" : "like"}> <i className="icon" /> </span> <span className={item.attitude === -1 ? "hate hated" : "hate"}> <i className="icon" /> </span> <span className="reply btn-hover">删除</span></div>
{ /* 排序 */}<div className="tabs-order"> <ul className="sort-container"> {state.tabs.map((item) => { return <li className="on">按热度排序</li>; })} </ul></div>;
{ /* 排序 */}<div className="tabs-order"> <ul className="sort-container"> {state.tabs.map((item) => { return ( <li key={item.id} className="on"> 按{item.name}排序 </li> ); })} </ul></div>;
<li key={item.id} className={item.type === state.active ? "on" : ""}> 按{item.name}排序</li>
react 组件
函数组件
npx create-react-app react-basic
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));root.render(<div>hello world</div>);
npm start
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数组件function HelloFunctionComponent() { return <h1>hello world</h1>;}root.render( <div> {/* 单标签 */} <HelloFunctionComponent /> {/* 双标签 */} <HelloFunctionComponent></HelloFunctionComponent> </div>);
- 组件的名称必须首字母大写(大驼峰的写法),react 内部会根据这个来判断是组件还是普通的 HTML 标签
- 函数组件必须有返回值,表示该组件的 UI 结构;
- 如果不需要渲染任何内容,则返回 null
- 组件就像 HTML 标签一样可以被渲染到页面中。
- 组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值
- 使用函数名称作为组件标签名称,可以是双标签也可以是单标签
类组件
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class HelloClassComponent extends React.Component { render() { return <div>这是我的第一个类组件!</div>; }}root.render( <div> {/* 渲染类组件 */} {/* 单标签 */} <HelloClassComponent /> {/* 双标签 */} <HelloClassComponent></HelloClassComponent> </div>);
- 类名称也必须以大写字母开头, 大驼峰写法
- 类组件应该继承
React.Component
父类,从而使用父类中提供的方法或属性 - 类组件必须提供
render
方法 render
方法必须有返回值,表示该组件的 UI 结构
事件绑定
<div onClick={() => {}}></div>
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数组件function HelloFun() { // 定义事件回调函数 const clickHandler = () => { alert("事件被触发了"); }; return ( // 绑定事件 <button onClick={clickHandler}>click me!</button> );}root.render( <div> {/* 单标签 */} <HelloFun /> {/* 多标签 */} <HelloFun></HelloFun> </div>);
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class HelloClassComponent extends React.Component { // 定义事件回调函数 clickHandler = () => { alert("事件被触发了"); }; render() { return ( // 绑定事件 <button onClick={this.clickHandler}>类组件 click me!</button> ); }}root.render( <div> {/* 渲染类组件 */} {/* 单标签 */} <HelloClassComponent /> {/* 双标签 */} <HelloClassComponent></HelloClassComponent> </div>);
// 定义事件回调函数 clickHandler() { alert("事件被触发了"); }
clickHandler() { console.log(this); // undefined }
clickHandler = () => { console.log(this); // HelloClassComponent};
获取事件对象
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数组件function HelloFun() { // 定义事件处理函数 const clickHandler = (e) => { // 阻止默认事件, 不让a标签跳转 e.preventDefault(); alert("点击事件已经被触发了..."); }; // 绑定点击事件 return ( <a onClick={clickHandler} href="https://www.baidu.com"> baidu </a> );}root.render( <div> {/* 单标签 */} <HelloFun /> </div>);
传递额外参数
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数组件function HelloFun() { // 定义事件回调函数 const clickHandler = () => { alert("事件被触发了"); }; return ( // 绑定事件 <button onClick={clickHandler}>click me!</button> );}root.render( <div> {/* 单标签 */} <HelloFun /> </div>);
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数组件function HelloFun() { // 定义事件回调函数 const clickHandler = (a, b) => { alert("事件被触发了"); alert(a); alert(b); }; return ( // 绑定事件 <button onClick={() => { clickHandler("hello", "world"); }} > click me! </button> );}root.render( <div> {/* 单标签 */} <HelloFun /> </div>);
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数组件function HelloFun() { // 定义事件回调函数 const clickHandler = (e, a, b) => { console.log(e); alert("事件被触发了"); alert(a); alert(b); }; return ( // 绑定事件 <button onClick={(e) => { clickHandler(e, "hello", "world"); }} > click me! </button> );}root.render( <div> {/* 单标签 */} <HelloFun /> </div>);
类组件的事件绑定
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class HelloClassComponent extends React.Component { // 定义事件回调函数 clickHandler = () => { alert("事件被触发了"); }; render() { return ( // 绑定事件 <button onClick={this.clickHandler}>类组件 click me!</button> ); }}root.render( <div> {/* 单标签 */} <HelloClassComponent /> </div>);
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class HelloClassComponent extends React.Component { // 定义事件回调函数 clickHandler = (a, b) => { alert("事件被触发了"); alert(a); alert(b); }; render() { return ( // 绑定事件 <button onClick={() => { this.clickHandler("hello", "world"); }} > 类组件 click me! </button> ); }}root.render( <div> {/* 单标签 */} <HelloClassComponent /> </div>);
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class HelloClassComponent extends React.Component { // 定义事件回调函数 clickHandler = (e, a, b) => { console.log(e); console.log(a, b); }; render() { return ( // 绑定事件 <button onClick={(e) => { this.clickHandler(e, "hello", "world"); }} > 类组件 click me! </button> ); }}root.render( <div> {/* 单标签 */} <HelloClassComponent /> </div>);
组件状态
初始化状态
state = { count: 0,};
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class Counter extends React.Component { // 定义状态 state = { count: 0, }; render() { return <button>计数器</button>; }}root.render( <div> <Counter></Counter> </div>);
读取状态
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class Counter extends React.Component { // 定义状态 state = { count: 0, }; render() { return ( <> <button>计数器</button> <p>当前计数: {this.state.count}</p> </> ); }}root.render( <div> <Counter></Counter> </div>);
修改状态
this.setState({ 要修改的部分数据 });
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class Counter extends React.Component { // 定义状态 state = { count: 0, }; changeCount = () => { this.setState({ count: this.state.count + 1, }); }; render() { return ( <> <button onClick={this.changeCount}>计数器</button> <p>当前计数: {this.state.count}</p> </> ); }}root.render( <div> <Counter></Counter> </div>);
React 的状态不可变
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 定义类组件class Counter extends React.Component { // 定义状态 state = { count: 0, }; changeCount = () => { this.setState({ count: this.state.count + 1, }); }; render() { return ( <> <button onClick={this.changeCount}>计数器</button> <p>当前计数: {this.state.count}</p> </> ); }}root.render( <div> <Counter></Counter> </div>);
changeCount = () => { this.setState({ count: this.state.count++, });};
state = { count: 0, list: [1, 2, 3], person: { name: "jack", age: 18, },};// 直接修改简单类型Numberthis.state.count++;++this.state.count;this.state.count += 1;this.state.count = 1;
// 直接修改数组this.state.list.push(123);this.state.list.slice(1, 1);
// 直接修改对象this.state.person.name = "rose";
this.setState({ count: this.state.count + 1 list: [...this.state.list, 4], person: { ...this.state.person, // 覆盖原来的属性 就可以达到修改对象中属性的目的 name: 'rose' }})
// 类组件class HelloClassComponent extends React.Component { state = { list: [1, 2, 3], }; changeState = () => { this.setState({ list: this.state.list.push(4), // × // list: [...this.state.list, 4], // √ }); }; render() { return ( <> <button onClick={this.changeState}>click me</button> <p>当前state: {JSON.stringify(this.state.list)}</p> </> ); }}
// 类组件class HelloClassComponent extends React.Component { state = { person: { name: "jack", age: 18, }, }; changeState = () => { // 正确写法 this.setState({ person: { ...this.state.person, age: 19 }, }); }; render() { return ( <> <button onClick={this.changeState}>click me</button> <p>当前state: {JSON.stringify(this.state.person)}</p> </> ); }}
表单处理
受控表单组件
// 引入Reactimport React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 定义类组件class InputComponent extends React.Component { // 1. 在组件的state中声明一个组件的状态数据, state = { message: "this is message", }; render() { // 2. 将input标签元素的value属性的值设置为状态数据 return <input type="text" value={this.state.message} />; }}root.render( <div> <InputComponent /> </div>);
// 定义类组件class InputComponent extends React.Component { // 1. 在组件的state中声明一个组件的状态数据, state = { message: "this is message", }; // 3.2 定义inputChange方法 inputChange = () => { console.log("input value is change..."); }; render() { // 2. 将input标签元素的value属性的值设置为状态数据 // 3.1 为input添加change事件 return <input type="text" onChange={this.inputChange} value={this.state.message} />; }}
inputChange = (e) => { // 4. 在事件处理程序中,通过事件对象e获取到当前文本框的值(`即用户当前输入的值`) const value = e.target.value; console.log(value);};
inputChange = (e) => { // 4. 在事件处理程序中,通过事件对象e获取到当前文本框的值(`即用户当前输入的值`) const value = e.target.value; // 5. 调用setState方法,将文本框的值作为state状态的最新值 this.setState({ message: value, });};
非受控表单组件
// 1. 导入createRef 函数import React, { createRef } from "react";
class InputComponent extends React.Component { // 2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中 msgRef = createRef();}
// 定义类组件class InputComponent extends React.Component { // 2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中 msgRef = createRef(); render() { return ( <> {/* 3. 为input添加ref属性,值为msgRef */} <input ref={this.msgRef} /> </> ); }}
// 引入React// 1. 导入createRef 函数import { render } from "@testing-library/react";import React, { createRef } from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 定义类组件class InputComponent extends React.Component { // 2. 调用createRef函数,创建一个ref对象,存储到名为msgRef的实例属性中 msgRef = createRef(); // 4.2. 在按钮的事件处理程序中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值 changeHandler = () => { console.log(this.msgRef.current.value); }; render() { return ( <> {/* 3. 为input添加ref属性,值为msgRef */} <input ref={this.msgRef} /> {/* 4.1 添加点击按钮 */} <button onClick={this.changeHandler}>click</button> </> ); }}root.render( <div> <InputComponent /> </div>);
🚩 组件基础小练习
npm i或者yarn
npm run start或者npm start
switchTab = (type) => { // 点击之后, 把type给state的active this.setState({ active: type, });};
textareaChange = (e) => { this.setState({ comment: e.target.value, });};
// 相关的方法, 提交评论submitComment = () => { // 1. 获取评论内容 const comment = this.state.comment; // 2. 判断是否为空 if (comment.trim() === "") { alert("评论内容不能为空"); return; } // 3. 构造评论对象 const obj = { id: Date.now(), author: "匿名用户", comment: comment, time: Date.now(), attitude: 0, }; // 4. 更新状态 this.setState({ list: [obj, ...this.state.list], comment: "", });};
npm i uuid或者yarn add uuid
import { v4 as uuid } from "uuid";
a3dd49f0-b463-477f-8bd9-88284fd0242b
// 相关的方法, 删除评论deleteComment = (id) => { // 1. 获取原来的list const list = this.state.list; // 2. 过滤 const newList = list.filter((item) => { return item.id !== id; }); // 3. 更新状态 this.setState({ list: newList, });};
// 相关的方法, 点赞switchLike = (id) => { // 1. 获取原来的list const list = this.state.list; // 2. 遍历 const newList = list.map((item) => { if (item.id === id) { if (item.attitude === 1) { item.attitude = 0; } else { item.attitude = 1; } } return item; }); // 3. 更新状态 this.setState({ list: newList, });};
// 相关的方法, 踩switchHate = (id) => { // 1. 获取原来的list const list = this.state.list; // 2. 遍历 const newList = list.map((item) => { if (item.id === id) { if (item.attitude === -1) { item.attitude = 0; } else { item.attitude = -1; } } return item; }); // 3. 更新状态 this.setState({ list: newList, });};
React 组件通信
父传子
父传子 类组件
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 类子组件class SonClass extends React.Component { render() { return <div>我是类子组件</div>; }}// ParentClass 父组件 Son 子组件class ParentClass extends React.Component { render() { return ( <div> <SonClass /> </div> ); }}root.render( <> <ParentClass /> </>);
// ParentClass 父组件 Son 子组件class ParentClass extends React.Component { state = { message: "this is message from parent", }; render() { return ( <div> <SonClass /> </div> ); }}
// 类子组件class SonClass extends React.Component { render() { return <div>我是类子组件, {this.props.msg}</div>; }}
父传子 函数式组件
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数式子组件function SonFun() { return <div>我是函数子组件</div>;}// ParentFun 父组件 Son 子组件class ParentFun extends React.Component { render() { return ( <div> <SonFun /> </div> ); }}root.render( <> <ParentFun /> </>);
// ParentFun 父组件 Son 子组件class ParentFun extends React.Component { state = { message: "this is message from parent", }; render() { return ( <div> <SonFun /> </div> ); }}
// 函数式子组件function SonFun(props) { return <div>我是函数子组件, {props.msg}</div>;}
props 说明
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数式子组件function SonFun(props) { return ( <div> 我是函数子组件 <ul> {props.list.map((item, index) => { return <li key={index}>{item}</li>; })} </ul> </div> );}// 类子组件class SonCls extends React.Component { render() { return ( <div> 我是类子组件 <ul> {this.props.list.map((item, index) => { return <li key={index}>{item}</li>; })} </ul> </div> ); }}
// Parent 父组件 Son 子组件class Parent extends React.Component { // 准备数据 state = { list: [1, 2, 3, 4], }; render() { return ( <div> <SonFun list={this.state.list} /> <SonCls list={this.state.list} /> </div> ); }}root.render( <> <Parent /> </>);
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数式子组件function SonFun(props) { return <div>我是函数子组件 ,我叫{props.userinfo.username}</div>;}// 类子组件class SonCls extends React.Component { render() { return <div>我是类子组件,我叫{this.props.userinfo.username}</div>; }}// Parent 父组件 Son 子组件class Parent extends React.Component { // 准备数据 state = { userinfo: { username: "lisi", age: 18, }, }; render() { return ( <div> <SonFun userinfo={this.state.userinfo} /> <SonCls userinfo={this.state.userinfo} /> </div> ); }}root.render( <> <Parent /> </>);
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数式子组件function SonFun(props) { return ( <> <div>这是子组件SonFun</div> <button onClick={props.getMsg}>点击</button> </> );}// 类子组件class SonCls extends React.Component { render() { return ( <> <div>这是子组件SonFun</div> <button onClick={this.props.getMsg}>点击</button> </> ); }}// Parent 父组件 Son 子组件class Parent extends React.Component { getMsg = () => { console.log("父组件函数"); }; render() { return ( <div> <SonFun getMsg={this.getMsg} /> <SonCls getMsg={this.getMsg} /> </div> ); }}root.render( <> <Parent /> </>);
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数式子组件function SonFun(props) { return ( <> <div>这是子组件SonFun</div> {props.child} </> );}// 类子组件class SonCls extends React.Component { render() { return ( <> <div>这是子组件SonFun</div> {this.props.child} </> ); }}// Parent 父组件 Son 子组件class Parent extends React.Component { render() { return ( <div> <SonFun child={<div>this is div</div>} /> <SonCls child={<div>this is div</div>} /> </div> ); }}root.render( <> <Parent /> </>);
子传父
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
// 函数子组件function SonFun() { return <div>this is son</div>;}// 父组件class Parent extends React.Component { render() { return ( <> <SonFun /> </> ); }}
root.render( <> <Parent /> </>);
// 父组件class Parent extends React.Component { // 1. 父组件提供一个回调函数 - 用于接收数据 getSonMsg = (sonMsg) => { console.log(sonMsg); }; render() { return ( <> {/* 2. 将函数作为属性的值,传给子组件 */} <SonFun getMsg={this.getSonMsg} /> </> ); }}
// 函数子组件function SonFun(props) { // 3. 子组件通过 props 调用回调函数 const { getMsg } = props; return <div>this is son</div>;}
// 函数子组件function SonFun(props) { // 3. 子组件通过 props 调用回调函数 const { getMsg } = props; // 4. 将子组件中的数据作为参数传递给回调函数 return ( <> <div>this is son </div> <button onClick={() => getMsg("这是子组件的数据")}>点击传递数据给父组件</button> </> );}
兄弟组件通信
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 函数式子组件function SonA(props) { return <div>this is SonA</div>;}function SonB(props) { return <div>this is SonB</div>;}// Parent 父组件 Son 子组件class Parent extends React.Component { render() { return ( <div> <SonA /> <SonB /> </div> ); }}root.render( <> <Parent /> </>);
// Parent 父组件 Son 子组件class Parent extends React.Component { getBMsg = (msg) => { console.log(msg); }; render() { return ( <div> <SonA /> <SonB getBMsg={this.getBMsg} /> </div> ); }}
function SonB(props) { const { getBMsg } = props; return ( <div> this is SonB <button onClick={() => getBMsg("这是B组件的数据")}>点击传值</button> </div> );}
// Parent 父组件 Son 子组件class Parent extends React.Component { getBMsg = (msg) => { console.log(msg); }; state = { sendAMsg: "测试一下父传子, 初始值...", }; render() { return ( <div> <SonA sendAMsg={this.state.sendAMsg} /> <SonB getBMsg={this.getBMsg} /> </div> ); }}
// 函数式子组件function SonA(props) { return <div>this is SonA {props.sendAMsg}</div>;}
getBMsg = (msg) => { this.setState({ sendAMsg: msg, });};
跨组件通信 Context
const { Provider, Consumer } = createContext();
<Provider value={this.state.message}>{/* 根组件 */}</Provider>
<Consumer > {value => /* 基于 context 值进行渲染*/}</Consumer>
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));function ComponentC() { return <div>this is ComponentC</div>;}function ComponentA() { return ( <div> this is ComponentA <ComponentC /> </div> );}class Parent extends React.Component { render() { return ( <div> this is Parent <ComponentA /> </div> ); }}root.render( <> <Parent /> </>);
// 2. 如果没有导入, 记得导入createContextimport React, { createContext } from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 1. 获取Provider, Consumerconst { Provider, Consumer } = createContext();
class Parent extends React.Component { // 3. 定义需要传递的数据 state = { message: "这是根组件Parent要传递的数据...", }; render() { return ( <Provider value={this.state.message}> <div> this is Parent <ComponentA /> </div> </Provider> ); }}
function ComponentC() { return ( <div> this is ComponentC, <Consumer> {/* 使用函数获取数据, 参数value是传过来的数据 */} {(value) => <b>{value}</b>} </Consumer> </div> );}
🚩 组件小练习
[ { id: 1, name: "超级好吃的棒棒糖", price: 18.8, info: "开业大酬宾,全场8折" }, { id: 2, name: "超级好吃的大鸡腿", price: 34.2, info: "开业大酬宾,全场8折" }, { id: 3, name: "超级无敌的冰激凌", price: 14.2, info: "开业大酬宾,全场8折" },];
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));// 子组件function ListItem(props) { return <div>this is ListItem</div>;}// 父组件class App extends React.Component { render() { return ( <> <ListItem /> </> ); }}root.render( <> <App /> </>);
// 父组件class App extends React.Component { state = { list: [ { id: 1, name: "超级好吃的棒棒糖", price: 18.8, info: "开业大酬宾,全场8折" }, { id: 2, name: "超级好吃的大鸡腿", price: 34.2, info: "开业大酬宾,全场8折" }, { id: 3, name: "超级无敌的冰激凌", price: 14.2, info: "开业大酬宾,全场8折" }, ], }; render() { return ( <> <ListItem /> </> ); }}
render() { return ( <> {this.state.list.map((item) => ( <ListItem key={item.id} {...item} /> ))} </> ); }
function ListItem(props) { const { name, price, info } = props; return ( <div> <h3>{name}</h3> <p>{price}</p> <p>{info}</p> </div> );}
delHandler = (id) => { this.setState({ list: this.state.list.filter((item) => item.id !== id), });};
<ListItem key={item.id} {...item} delHandler={this.delHandler} />
<button onClick={() => delHandler(id)}>删除</button>
children 属性
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function ComponentA(props) { console.log(props.children); return <div>this is A</div>;}
class Parent extends React.Component { render() { return ( <ComponentA> <div>hello world</div> </ComponentA> ); }}
root.render( <> <Parent /> </>);
function ComponentA(props) { const { children } = props; return <div>this is A {children}</div>;}
class Parent extends React.Component { render() { return <ComponentA>这是一段文本</ComponentA>; }}
function ComponentA(props) { const { children } = props; return <div>this is A {children}</div>;}
class Parent extends React.Component { render() { return ( <ComponentA> <b>这是一个加粗标签</b> </ComponentA> ); }}
function ComponentA(props) { const { children } = props; return <div>this is A{children.map((item) => item)}</div>;}
class Parent extends React.Component { render() { return ( <ComponentA> <b>这是一个加粗标签1</b> <b>这是一个加粗标签2</b> <b>这是一个加粗标签3</b> </ComponentA> ); }}
function ComponentA(props) { const { children } = props; return <div>this is A {children()}</div>;}
class Parent extends React.Component { render() { return <ComponentA>{() => "我是一个函数..."}</ComponentA>; }}
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function ComponentA(props) { const { children } = props; return <div>this is A {children}</div>;}
class Parent extends React.Component { render() { return ( <ComponentA> { <div> <b>{"this is jsx"}</b> </div> } </ComponentA> ); }}
root.render( <> <Parent /> </>);
props 校验-场景和使用
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function Son() { return ( <ul> <li>子组件</li> </ul> );}
class Parent extends React.Component { render() { return ( <div> <Son /> </div> ); }}
root.render( <> <Parent /> </>);
class Parent extends React.Component { render() { return ( <div> <Son colors={["red", "blue", "yellow"]} /> </div> ); }}
function Son(props) { const colorArr = props.colors; const list = colorArr.map((item, index) => { return <li key={index}>{item}</li>; }); return <ul>{list}</ul>;}
npm install prop-types或者yarn add prop-types
import React from "react";import ReactDOM from "react-dom/client";// 导入 prop-types包import PropTypes from "prop-types";const root = ReactDOM.createRoot(document.getElementById("root"));
Son.propTypes = { colors: PropTypes.array,};
return ( <div> <Son colors={18} /> </div>);
props 校验-规则说明
// 常见类型optionalFunc: PropTypes.func,// 必填 只需要在类型后面串联一个isRequiredrequiredFunc: PropTypes.func.isRequired,// 特定结构的对象optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number})
import React from "react";import ReactDOM from "react-dom/client";// 导入 prop-types包import PropTypes from "prop-types";const root = ReactDOM.createRoot(document.getElementById("root"));function Son(props) { const colorArr = props.colors; const list = colorArr.map((item, index) => { return <li key={index}>{item}</li>; }); return <ul>{list}</ul>;}
Son.propTypes = { // 必须是数组, 而且是必传 colors: PropTypes.array.isRequired,};class Parent extends React.Component { render() { return ( <div> {/* 没有传值 */} <Son /> </div> ); }}root.render( <> <Parent /> </>);
props 校验-默认值
props 校验-默认值-函数组件
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));function Son() { return <h1>函数子组件</h1>;}class Parent extends React.Component { render() { return ( <div> <Son /> </div> ); }}root.render( <> <Parent /> </>);
npm install prop-types或者yarn add prop-types
import PropTypes from "prop-types";
// 2. 定义类型校验Son.propTypes = { colors: PropTypes.array,};
// 3. 定义默认值, 如果父组件不进行传值, 走默认Son.defaultProps = { colors: ["aaa", "bbb", "ccc", "ddd"],};
return ( <div> <Son colors={["red", "yellow", "blue"]} /> </div>);
function Son(props) { const colorArr = props.colors; const list = colorArr.map((item, index) => { return <li key={index}>{item}</li>; }); return <ul>{list}</ul>;}
// 5. 子组件接收值function Son({ colors = ["aaa", "bbb", "ccc", "ddd", "eee"] }) { const colorArr = colors; const list = colorArr.map((item, index) => { return <li key={index}>{item}</li>; }); return <ul>{list}</ul>;}
props 校验-默认值-类组件
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));class Son extends React.Component { render() { return <h1>这是子组件</h1>; }}class Parent extends React.Component { render() { return ( <div> <Son /> </div> ); }}root.render( <> <Parent /> </>);
npm install prop-types或者yarn add prop-types
import PropTypes from "prop-types";
// 2. 给默认值Son.defaultProps = { colors: ["aaa", "bbb", "ccc"],};
// 3. 子组件展示数据class Son extends React.Component { render() { return ( <ul> {this.props.colors.map((item, index) => { return <li key={index}>{item}</li>; })} </ul> ); }}
// 类静态属性声明默认值 static defaultProps = { colors: ["11111", "22222", "33333"], };
生命周期
React Lifecycle Methods diagram
生命周期 - 挂载阶段
钩子 函数 | 触发时机 | 作用 |
---|---|---|
constructor | 创建组件时,最先执行,初始化的时候只执行一次 | 1. 初始化 state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等 |
render | 每次组件渲染都会触发 | 渲染 UI(注意: 不能在里面调用 setState() , 会无限循环) |
componentDidMount | 组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次 | 1. 发送网络请求 2.DOM 操作 |
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));class Parent extends React.Component { render() { return <div>hello world</div>; }}root.render( <> <Parent /> </>);
class Parent extends React.Component { // 创建组件时,最先执行,初始化的时候只执行一次 constructor() { super(); console.log("constructor...创建组件时,最先执行,初始化的时候只执行一次"); } // 组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次 componentDidMount() { console.log("componentDidMount...组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次"); } // 每次组件渲染都会触发 render() { console.log("render...每次组件渲染都会触发"); return <div>hello world</div>; }}
state = { count: 0,};
return <button onClick={this.handleClick}>{this.state.count}</button>;
handleClick = () => { this.setState({ count: this.state.count + 1, });};
生命周期 - 更新阶段
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次组件渲染都会触发 | 渲染 UI(与 挂载阶段 是同一个 render) |
componentDidUpdate | 组件更新后(DOM 渲染完毕) | DOM 操作,可以获取到更新后的 DOM 内容,不要直接调用 setState |
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));class Parent extends React.Component { state = { count: 0, }; // 创建组件时,最先执行,初始化的时候只执行一次 constructor() { super(); console.log("constructor...创建组件时,最先执行,初始化的时候只执行一次"); } // 组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次 componentDidMount() { console.log("componentDidMount...组件挂载(完成 DOM 渲染)后执行,初始化的时候执行一次"); } handleClick = () => { this.setState({ count: this.state.count + 1, }); }; // 每次组件渲染都会触发 render() { console.log("render...每次组件渲染都会触发"); return <button onClick={this.handleClick}>{this.state.count}</button>; }}root.render( <> <Parent /> </>);
// 组件更新后(DOM 渲染完毕) componentDidUpdate() { console.log("componentDidUpdate...组件更新后(DOM 渲染完毕)"); }
生命周期 - 卸载阶段
钩子函数 | 触发时机 | 作用 |
---|---|---|
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作(比如:清理定时器等) |
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));class Son extends React.Component { render() { return "this is son"; }}class Parent extends React.Component { render() { return ( <div> <Son /> </div> ); }}root.render( <> <Parent /> </>);
// 组件卸载(从页面中消失) componentWillUnmount() { console.log("componentWillUnmount...组件卸载(从页面中消失)"); }
state = { flag: true,};
handleClick = () => { this.setState({ flag: false, });};
<div> {this.state.flag ? <Son /> : ""} <button onClick={this.handleClick}>click</button></div>
🚩 阶段小练习-todolist
npm i或者yarn
npm start或者yarn start
npm run mock-serve或者yarn mock-serve
接口作用 | 接口地址 | 接口方法 | 接口参数 |
---|---|---|---|
获取列表 | http://localhost:3001/data | GET | 无 |
删除 | http://localhost:3001/data/ | DELETE | id |
搜索 | http://localhost:3001/data/?q=keyword | GET | name(以 name 字段搜索) |
功能 | 核心思路 |
---|---|
表格数据渲染 | 组件使用 |
删除功能 | 获取当前 id 调用接口 |
搜索功能 | 用的依旧是列表接口,多传一个 q 参数 |
清除搜索功能 | 清空搜索参数 重新获取列表 |
import axios from "axios";
接口作用 | 接口地址 | 接口方法 | 接口参数 |
---|---|---|---|
获取列表 | http://localhost:3001/data | GET | 无 |
删除 | http://localhost:3001/data/ | DELETE | id |
搜索 | http://localhost:3001/data/?q=keyword | GET | name(以 name 字段搜索) |
// 加载列表 loadList = async () => { const res = await axios.get("http://localhost:3001/data"); console.log(res); }; componentDidMount() { this.loadList(); }
loadList = async () => { const res = await axios.get("http://localhost:3001/data"); this.setState({ list: res.data, });};
rowKey={"id"}
render: (text, record) => ( <Space size="middle"> <Popconfirm title="确定要删除吗?" onConfirm={() => this.handleDelete(record.id)}> <a href="#">删除</a> </Popconfirm> </Space> ),
handleDelete = () => { alert("开始删除...");};
handleDelete = (id) => { alert("开始删除, id为" + id);};
接口作用 | 接口地址 | 接口方法 | 接口参数 |
---|---|---|---|
获取列表 | http://localhost:3001/data | GET | 无 |
删除 | http://localhost:3001/data/ | DELETE | id |
搜索 | http://localhost:3001/data/?q=keyword | GET | name(以 name 字段搜索) |
// 删除handleDelete = async (id) => { // 调用删除接口 await axios.delete("http://localhost:3001/data/" + id); // 重新拉取列表 this.loadList();};
// 搜索onSearch = (value) => { alert(value);};
{ "data": [ { "id": 1, "name": "吃饭", "des": "干饭人干饭魂" }, { "id": 2, "name": "睡觉", "des": "不如睡觉写代码不如睡觉" }, { "id": 3, "name": "打豆豆", "des": "不如睡觉写代码不如睡觉" }, { "id": 4, "name": "写vue代码", "des": "不如睡觉写代码不如睡觉" } ]}
接口作用 | 接口地址 | 接口方法 | 接口参数 |
---|---|---|---|
获取列表 | http://localhost:3001/data | GET | 无 |
删除 | http://localhost:3001/data/ | DELETE | id |
搜索 | http://localhost:3001/data/?q=keyword | GET | name(以 name 字段搜索) |
// 搜索onSearch = async (value) => { const res = await axios.get("http://localhost:3001/data/?q=" + value); this.setState({ list: res.data, });};
什么是 hooks
Hooks 解决了什么问题
useState
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function App() { return <div>这是一个函数组件</div>;}root.render( <> <App /> </>);
// 1. 导入 `useState` 函数import { useState } from "react";
function App() { // 2. 调用 useState 函数,并传入状态的初始值, 返回值:数组,包含两个值:状态值和修改该状态的函数 const result = useState(0); return <div>这是一个函数组件</div>;}
// 3. 从 useState 函数的返回值中,拿到状态和修改状态的方法const [count, setCount] = result;
return ( <div> <button>增加</button> {/* 4. 在 JSX 中展示状态 */} <div>当前计数: {count}</div> </div>);
const handleClick = (number) => { // 5. 调用修改状态的方法更新状态 setCount(number);};
<button onClick={() => { handleClick(count + 1); }}> 增加</button>
组件的更新过程
const [count, setCount] = useState(0);const [list, setList] = useState([]);
// 1. 导入 `useState` 函数import { useState } from "react";import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() { const [count, setCount] = useState(0); const [list, setList] = useState([]); const handleClick = () => { // count 自增 setCount(count + 1); // 生成一个新数组, 包含原来数组的值, 以及最新的count const newList = [...list, count]; // 更新数组 setList(newList); }; return ( <div> <button onClick={handleClick}>增加</button> {/* 在 JSX 中展示状态 */} <div>当前计数: {count}</div> <div>当前数组: {list.join("---")}</div> </div> );}root.render( <> <App /> </>);
函数副作用
const arr = [6, 5, 4, 3, 2, 1];const arr1 = arr.sort();console.log(arr1);console.log(arr);
const arr = [6, 5, 4, 3, 2, 1];const arr1 = arr.slice().sort();console.log(arr1);console.log(arr);
useEffect
import { useState } from "react";import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() { const [count, setCount] = useState(0); return ( <div> <button onClick={() => setCount(count + 1)}>增加</button> <div>{count}</div> </div> );}root.render( <> <App /> </>);
// 导入 `useEffect` 函数import { useEffect, useState } from "react";
// 2. 调用 useEffect 函数,并传入回调函数useEffect(() => { console.log("副作用执行了....");});
useEffect(() => { console.log("副作用执行了...."); // 3. 在回调函数中编写副作用处理(dom 操作) document.title = count + "!!!!";});
依赖项控制执行时机
// 导入 `useEffect` 函数import { useEffect, useState } from "react";import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() { const [count, setCount] = useState(0); // 2. 调用 useEffect 函数,并传入回调函数 useEffect(() => { console.log("副作用执行了...."); // 3. 在回调函数中编写副作用处理(dom 操作) document.title = count + "!!!!"; }); return ( <div> <button onClick={() => setCount(count + 1)}>增加</button> <div>{count}</div> </div> );}root.render( <> <App /> </>);
useEffect(() => { console.log("副作用执行了...."); // 3. 在回调函数中编写副作用处理(dom 操作) document.title = count + "!!!!";}, []);
// 导入 `useEffect` 函数import { useEffect, useState } from "react";import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
function App() { const [count, setCount] = useState(0); const [name, setName] = useState("张三"); // 2. 调用 useEffect 函数,并传入回调函数 useEffect(() => { // 3. 在回调函数中编写副作用处理(dom 操作) console.log("副作用执行了...."); document.title = count + "!!!!"; }, [count]); return ( <div> <button onClick={() => setCount(count + 1)}>增加</button> <div>{count}</div> <button onClick={() => setName("李四")}>改名字</button> <div>{name}</div> </div> );}root.render( <> <App /> </>);
🚩 自定义 hook 小练习 1
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));function App() { return <div>这是一个组件</div>;}root.render( <> <App /> </>);
// 引入 React 的 useState 钩子import { useState } from "react";
// 定义一个名为 useWindowScroll 的自定义钩子函数export function useWindowScroll() { // 使用 useState 钩子创建一个状态变量 y 和设置状态的函数 setY,并将初始值设为 0 const [y, setY] = useState(0);
// 添加滚动事件监听器到 window 对象上 window.addEventListener("scroll", () => { // 获取当前文档顶部相对于视口的垂直偏移量 const h = document.documentElement.scrollTop; // 更新状态变量 y 的值为获取到的偏移量 h setY(h); });
// 返回状态变量 y,以便在组件中使用 return y;}
import ReactDOM from "react-dom/client"; // 导入React DOM的客户端模块import { useWindowScroll } from "./useWindowScroll"; // 导入自定义的useWindowScroll钩子函数const root = ReactDOM.createRoot(document.getElementById("root")); // 创建根节点function App() { const y = useWindowScroll(); // 使用useWindowScroll钩子获取滚动位置 return <div style={{ height: "12000px" }}>{y}</div>; // 返回一个包含滚动位置的div元素}root.render( <> <App /> // 渲染App组件 </>);
🚩 自定义 hook 小练习 2
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));function App() { return <div>这是一个组件</div>;}root.render( <> <App /> </>);
import { useEffect, useState } from "react";
export function useLocalStorage(key, defaultValue) { const [message, setMessage] = useState(defaultValue); // 每次只要message变化 就会自动同步到本地localStorage useEffect(() => { window.localStorage.setItem(key, message); }, [message, key]); return [message, setMessage];}
import ReactDOM from "react-dom/client"; // 导入ReactDOM模块中的createRoot函数import { useLocalStorage } from "./useLocalStorage"; // 导入自定义的useLocalStorage钩子函数const root = ReactDOM.createRoot(document.getElementById("root")); // 创建根节点function App() { const [message, setMessage] = useLocalStorage("username", "zhangsan"); // 使用useLocalStorage钩子,获取本地存储的值并设置默认值为"zhangsan" const changeName = () => { setMessage("lisi"); // 点击按钮后,修改message的值为"lisi" }; return ( <> {/* 在页面上显示message的值 */} <div>{message}</div> {/* 点击按钮执行changeName函数,修改message的值 */} <button onClick={changeName}>改名字</button> </> );}root.render( <> {/* 渲染App组件 */} <App /> </>); // 将根节点中渲染的内容进行更新操作
useState - 回调函数作为参数
const [name, setName] = useState(() => { // 编写计算逻辑 return '计算之后的初始值'});
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function Counter(props) { const { count } = props; return ( <div> <button>{count}</button> </div> );}
root.render( <> <Counter count={10} /> <Counter count={20} /> </>);
import { useState } from "react";// ...
const [count, setCount] = useState(() => { // 使用函数的返回值, 作为初始值 return props.count;});
<button onClick={() => setCount(count + 1)}>{count}</button>
import ReactDOM from "react-dom/client";import { useState } from "react";const root = ReactDOM.createRoot(document.getElementById("root"));
function Counter(props) { const [count, setCount] = useState(() => { // 使用函数的返回值, 作为初始值 return props.count; }); return ( <div> <button onClick={() => setCount(count + 1)}>{count}</button> </div> );}
root.render( <> <Counter count={10} /> <Counter count={20} /> </>);
useEffect - 清理副作用
useEffect(() => { console.log("副作用函数执行了"); // 副作用函数的执行时机为: 在下一次副作用函数执行之前执行 return () => { console.log("清理副作用的函数执行了"); // 在这里写清理副作用的代码 };});
import { useEffect, useState } from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function Son() { useEffect(() => { // 函数副作用 setInterval(() => { console.log("副作用函数执行了"); }, 1000); }); return <div>this is son</div>;}
function Parent() { const [flag, setFlag] = useState(true); return ( <> <button onClick={() => setFlag(!flag)}>click</button> {flag ? <Son /> : null} </> );}
root.render( <> <Parent /> </>);
useEffect(() => { const timeId = setInterval(() => { console.log("副作用执行了..."); }, 1000); // 清理副作用的代码, 组件销毁后会自动触发 return () => { clearInterval(timeId); };});
useEffect - 发送网络请求
{ "code": "0000", "data": [ { "id": 1, "name": "全部商品-番茄150g/份", "imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261527635.png", "sales": 1, "promotionPrice": 1.6, "originalPrice": 1.6 }, { "id": 2, "name": "全部商品-番茄250g/份", "imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261527853.png", "sales": 2, "promotionPrice": 2.6, "originalPrice": 2.6 }, { "id": 3, "name": "全部商品-番茄350g/份", "imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261527922.png", "sales": 3, "promotionPrice": 3.6, "originalPrice": 3.6 }, { "id": 4, "name": "全部商品-番茄450g/份", "imgUrl": "https://markdown-1253389072.cos.ap-nanjing.myqcloud.com/202202261530259.png", "sales": 4, "promotionPrice": 4.6, "originalPrice": 4.6 } ], "desc": "成功"}
useEffect(async () => { const res = await axios.get("http://xxx.com/demo"); console.log(res);}, []);
useEffect(() => { async function fetchData() { const res = await axios.get("http://xxx.com/demo"); console.log(res); } fetchData();}, []);
npm install axios或者yarn add axios
import axios from "axios";import { useEffect, useState } from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function App() { useEffect(() => { async function getData() { const response = await axios.get( "https://www.fastmock.site/mock/65fd811567052bc43a50412985949ab6/api/shop/1/tab/all" ); console.log(response); } getData(); }, []); return <></>;}
root.render( <> <App /> </>);
npm run start
useEffect(async () => { const response = await axios.get( "https://www.fastmock.site/mock/65fd811567052bc43a50412985949ab6/api/shop/1/tab/all" ); console.log(response);}, []);
useRef
import React from "react";import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
class Son extends React.Component { render() { return <div>this is son</div>; }}
function Parent() { return ( <div> <Son /> <h1>this is h1</h1> </div> );}
root.render( <> <Parent /> </>);
// 1. 导入 useRef 函数import { useRef } from "react";
// 2. 执行 useRef 函数并传入 null,返回值为一个对象, 内部有一个 current 属性存放拿到的 dom 对象(组件实例)const sonRef = useRef(null);const h1Ref = useRef(null);
import React, { useEffect, useRef } from "react";
useEffect(() => { console.log(sonRef.current); console.log(h1Ref.current);});
useContext
import ReactDOM from "react-dom/client";const root = ReactDOM.createRoot(document.getElementById("root"));
function ComponentA() { return ( <div> this is component A <ComponentC /> </div> );}function ComponentC() { return <div>this is component C</div>;}
function Parent() { return ( <div> <ComponentA /> </div> );}
root.render( <> <Parent /> </>);
import { createContext } from "react";// ...// 1. 使用`createContext` 创建 Context 对象const Context = createContext();
import { createContext, useState } from "react";
function Parent() { const [count] = useState(0); return ( // 在顶层组件通过`Provider` 提供数据 <Context.Provider value={count}> <div> <ComponentA /> </div> </Context.Provider> );}
import { createContext, useContext, useState } from "react";
function ComponentC() { // 3. 在底层组件通过`useContext`函数获取数据 const count = useContext(Context); return <div>this is component C, count is : {count}</div>;}
function Parent() { const [count, setCount] = useState(0); return ( // 2. 在顶层组件通过 Provider 提供数据 <Context.Provider value={count}> <div> <button onClick={() => { setCount(count + 1); }} > click </button> <ComponentA /> </div> </Context.Provider> );}
React-Router 前置知识
单页面应用 SPA
路由的本质
前端路由 vs 后端路由
准备路由项目环境
vite 简介
搭建一个 Vite 项目
npm create vite@latest或者yarn create vite
npm i或者yarn
npm run dev或者yarn dev
安装 react-router
npm i react-router-dom或者yarn add react-router-dom
基础使用
// 引入必要的内置组件import { BrowserRouter, Routes, Route, Link } from "react-router-dom";// 准备两个路由组件const Home = () => <div>home</div>;const About = () => <div>about</div>;function App() { return ( <div className="App"> <BrowserRouter> <Link to="/"> <button>首页</button> </Link> <Link to="/about"> <button>关于</button> </Link> <Routes> <Route path="/" element={<Home />}></Route> <Route path="/about" element={<About />}></Route> </Routes> </BrowserRouter> </div> );}export default App;
// 引入必要的内置组件import { BrowserRouter, Routes, Route, Link } from "react-router-dom";// 准备两个路由组件const Home = () => <div>home</div>;const About = () => <div>about</div>;function App() { return ( <div className="App"> {/* 声明当前要用一个非hash模式的路由 */} <BrowserRouter> {/* 指定跳转的组件, to用来配置路由地址 */} <Link to="/"> <button>首页</button> </Link> <Link to="/about"> <button>关于</button> </Link> {/* 路由出口, 路由对应的组件会在这里进行渲染 */} <Routes> {/* 指定路径和组件的对应关系, path代表路径, element代表组件, 成对出现, path ==> element */} <Route path="/" element={<Home />}></Route> <Route path="/about" element={<About />}></Route> </Routes> </BrowserRouter> </div> );}export default App;
核心内置组件说明
BrowerRouter
模式 | 实现方式 | 路由 url 表现 |
---|---|---|
HashRouter | 监听 url hash 值实现 | http://localhost:3000/#/about |
BrowserRouter | h5 的 history.pushState API 实现 | http://localhost:3000/about |
// 引入必要的内置组件import { Routes, Route, Link, HashRouter } from "react-router-dom";// 准备两个路由组件const Home = () => <div>home</div>;const About = () => <div>about</div>;function App() { return ( <div className="App"> {/* 生命当前要用一个非hash模式的路由 */} <HashRouter> {/* 指定跳转的组件, to用来配置路由地址 */} <Link to="/"> <button>首页</button> </Link> <Link to="/about"> <button>关于</button> </Link> {/* 路由出口, 路由对应的组件会在这里进行渲染 */} <Routes> {/* 指定路径和组件的对应关系, path代表路径, element代表组件, 成对出现, path ==> element */} <Route path="/" element={<Home />}></Route> <Route path="/about" element={<About />}></Route> </Routes> </HashRouter> </div> );}export default App;
Link
Routes
Route
编程式导航
function Login() { return <div>login</div>;}
export default Login;
import Login from "./Login";
<Route path="/login" element={<Login />}></Route>
// 导入useNavigateimport { useNavigate } from "react-router-dom";
// 执行useNavigate, 得到一个跳转函数const navigate = useNavigate();
// 跳转到首页function goHome() { // 调用跳转函数, 传入目标路径 navigate("/");}
<button onClick={goHome}>跳转首页</button>
function goHome() { // 调用跳转函数, 传入目标路径 navigate("/", { replace: true });}
路由传参
searchParams 传参
function goHome() { // 调用跳转函数, 传入目标路径, searchParams传参 navigate("/?id=10001", { replace: true });}
const Home = () => { // 路由取参 const [params] = useSearchParams(); // params有一个get方法, 可以获取参数 const id = params.get("id"); return <div>home 用户id {id}</div>;};
params 传参
function goHome() { // 调用跳转函数, 传入目标路径, searchParams传参 navigate("/10001", { replace: true });}
const Home = () => { let params = useParams(); const id = params.id; return <div>home 用户id是 {id}</div>;};
嵌套路由
import { Outlet } from "react-router-dom";
function Layout() { return <div>layout</div>;}export default Layout;
function Board() { return <div>Board</div>;}export default Board;
function Article() { return <div>Article</div>;}export default Article;
import { BrowserRouter, Route, Routes } from "react-router-dom";import Article from "./Article";import Board from "./Board";import Layout from "./Layout";
function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout></Layout>}> <Route path="board" element={<Board></Board>}></Route> <Route path="article" element={<Article></Article>}></Route> </Route> </Routes> </BrowserRouter> );}export default App;
import { Outlet } from "react-router-dom";
function Layout() { return ( <div> layout {/* 二级路由的出口 */} <Outlet></Outlet> </div> );}export default Layout;
默认二级路由
import { BrowserRouter, Route, Routes } from "react-router-dom";import Article from "./Article";import Board from "./Board";import Layout from "./Layout";
function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout></Layout>}> <Route index element={<Board></Board>}></Route> <Route path="article" element={<Article></Article>}></Route> </Route> </Routes> </BrowserRouter> );}export default App;
404 路由配置
function NotFound() { return <div>NotFound</div>;}export default NotFound;
import { BrowserRouter, Route, Routes } from "react-router-dom";import Article from "./Article";import Board from "./Board";import Layout from "./Layout";import NotFound from "./NotFound";
function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout></Layout>}> <Route index element={<Board></Board>}></Route> <Route path="article" element={<Article></Article>}></Route> </Route> {/* 如果没有匹配成功, 走这个路由 */} <Route path="*" element={<NotFound />}></Route> </Routes> </BrowserRouter> );}export default App;
集中式路由配置
import { BrowserRouter, Route, Routes } from "react-router-dom";import Article from "./Article";import Board from "./Board";import Layout from "./Layout";import NotFound from "./NotFound";
function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout></Layout>}> <Route index element={<Board></Board>}></Route> <Route path="article" element={<Article></Article>}></Route> </Route> {/* 如果没有匹配成功, 走这个路由 */} <Route path="*" element={<NotFound />}></Route> </Routes> </BrowserRouter> );}export default App;
import { Outlet } from "react-router-dom";
function Layout() { return ( <div> layout {/* 二级路由的出口 */} <Outlet></Outlet> </div> );}export default Layout;
function Board() { return <div>Board</div>;}export default Board;
function Article() { return <div>Article</div>;}export default Article;
function NotFound() { return <div>NotFound</div>;}export default NotFound;
import { BrowserRouter, Route, Routes, useRoutes } from "react-router-dom";import Article from "./Article";import Board from "./Board";import Layout from "./Layout";import NotFound from "./NotFound";
// 1. 准备一个路由数组 数组中定义所有的路由对应关系const routesList = [ { path: "/", element: <Layout />, children: [ { element: <Board />, index: true, // index设置为true 变成默认的二级路由 }, { path: "article", element: <Article />, }, ], }, // 增加n个路由对应关系 { path: "*", element: <NotFound />, },];
// 2. 使用useRoutes方法传入routesList生成Routes组件function WrapperRoutes() { let element = useRoutes(routesList); return element;}
function App() { return ( <BrowserRouter> {/* 3. 替换之前的Routes组件 */} <WrapperRoutes /> </BrowserRouter> );}export default App;