React基础
📣 📣 📣 📢📢📢
☀️☀️点开就是缘分认识一下,我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。
📒 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️,擅长使用Java技术开发web项目和工具
📒 文章内容丰富:覆盖大部分java必学技术栈,前端,计算机基础,容器等方面的文章
📒 如果你也对Java感兴趣,关注小冷吧,一起探索Java技术的生态与进步,一起讨论Java技术的使用与学习
✏️高质量技术专栏专栏链接: 微服务,数据结构,netty,单点登录,SSM ,SpringCloudAlibaba等
😝公众号😝 : 想全栈的小冷 ,分享一些技术上的文章,以及解决问题的经验⏩当前专栏 :React系列
React核心价值与前置知识
核心价值
组件化(易开发易维护)
数据驱动视图 :定义好数据和ui的显示规则 即
UI=f(state)
- 只关注业务数据修改,不在操作DOM 增加开发效率
使用vite创建Recat项目
开发规范
使用 prettier & eslint
规范开发
- eslint 检查语法语义
- prettier 检查代码风格
shell
#eslint :
npm install eslint@typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
#prettier:
npm install prettier eslint-config-prettier eslint-plugin-prettier --save-dev
vite和 webpack的区别
webpack
是一个非常流行的前端打包工具 比较经典 Create-React-App
是使用webpack作为打包工具的
vite
既是构建工具 又是打包工具
vite
的特点:
- Vite打包项目 在启动和代码更新时更快
- vite使用了
es Module
语法(仅开发环境)
React JSX语法
内容 :
- JSX语法
- 组件和props
- 实战: 列表页
JSX
特点:
- JSX是js的扩展 写在js代码里面 组件的ui结构
- 语法和html很相似
- 不只是React独有
标签
- 首字母大小写的区别 , 大写字母是自定义组件
- 标签必须闭合 如
<input>
在jsx是非法的 - 每段JSX中只有一个根节点
属性
和html基本相似
- class要改为 className
- style要使用js对象 不能是string 而且key需要使用驼峰写法
如下
在JSX中插入js变量
- 使用
{}
可以插入JS变量 函数 表达式 - 可以插入文本 属性
- 可以用于注释
代码案例
条件判断
常见的if else
可以通过{}的方式实现,但是在JSX
中代码一多就显得不够实用了 以下三种方法可以解决:
- 使用
&&
- 使用三元表达式
- 使用函数来判断
比如这样:反之如果flag等于false 就不会出现hello
效果:
三元运算符:flag为判断条件 来控制标签的显示
效果:
函数:
jsx
function isShowHello(){
if (flag)return <p>show hello</p>
return <p>defaultHello</p>
}
效果 :
循环
- 使用map来循环
- 每一个循环项(item)都要有key
- key需要具有唯一性
实现
jsx
const list = [
{username:'zhangsan', name:"张三"},
{username:'shuangyue', name:"双月"},
{username:'lisi', name:"李四"},
]
{/*循环*/}
<div>
{list.map(user=>{
const {username,name} = user
return <li key={username}>{name}</li>
})}
</div>
效果:
PS : 不建议使用 index 如 :
因为我们的key 需要具有唯一性
小结实战 列表页
开发一个列表页
调整一下显示的jsx
保证这个代码结构简洁 ,然后就可以开始开发了
jsx
import React from 'react';
import './App1.css';
function App() {
const questionList = [
{id: 'q1', title: '问卷1', isPublished: true},
{id: 'q2', title: '问卷2', isPublished: true},
{id: 'q3', title: '问卷3', isPublished: true},
{id: 'q4', title: '问卷4', isPublished: false}
]
function edit(id) {
console.log('edit', id);
}
return (<div>
<h1>列表详情页</h1>
<div>
{questionList.map(question => {
const {id, title, isPublished} = question;
return <div key={id} className="list-item">
<strong>{title}</strong>
{isPublished ? <span style={{color: "green"}}>已发布</span> : <span>未发布</span>}
<button onClick={() => edit(id)}>编辑问卷</button>
</div>
})}
</div>
</div>)
}
export default App;
css
css
.list-item {
border: 1px solid #ccc;
padding: 10px;
margin-bottom: 16px;
display: flex;
justify-content: center;
}
效果
组件
react
一切皆是组件
- 组件拥有一个ui片段
- 拥有独立的逻辑和显示
- 可大可小 可以嵌套
组件拆分的价值和意义
- 组件嵌套来组织的 ui 结构 和 html 一样没有学习成本
- 良好的拆分组件利于代码维护和多人协同开发
- 封装公共组件或者直接使用第三方组件复用代码
好的组件化 逻辑是清晰的 更能提升开发效率并且更加的美观易读
我们可以将组件理解成一个一个的函数
使用我们之前的列表页代码 拆分成组件 list1
然后用improt的方式 引入到listdemo中
这样我们的总框架就没有那么多的代码冗余 需要修改对应的代码 只需要寻找对应的组件文件即可
属性 props
- 组件可以嵌套 有层级关系
- 父组件可以向子组件传递数据
- props是只读对象
props 其实就是实现差异化组件信息传递的一种手段
实践
将之前循环内显示数据的div拆出来抽象成组件:QuestCard.tsx
。 CSS还是和之前的内容一样
使用 ts主要是方便传入泛型
QuestCard.tsx
tsx
import React, {FC} from "react";
import './QuestCard.css'
type proptype = {
id: string,
title: string,
isPublished: boolean
}
export const QuestCard: FC<proptype> = (props) => {
const {id, title, isPublished} = props;
function edit(id) {
console.log('edit', id);
}
return (
<div key={id} className="list-item">
<strong>{title}</strong>
{isPublished ? <span style={{color: "green"}}>已发布</span> : <span>未发布</span>}
<button onClick={() => edit(id)}>编辑问卷</button>
</div>)
}
改造list1.jsx
这样就将显示问卷卡片抽取出来为一个独立的组件了
jsx
import React from "react";
import './list1.css';
import {QuestCard} from "./QuestCard";
export const List1 = () => {
const questionList = [
{id: 'q1', title: '问卷1', isPublished: true},
{id: 'q2', title: '问卷2', isPublished: true},
{id: 'q3', title: '问卷3', isPublished: true},
{id: 'q4', title: '问卷4', isPublished: false}
]
return (
<div>
<h1>列表详情页</h1>
<div>
{questionList.map(question => {
const {id, title, isPublished} = question;
return <QuestCard key={id} id={id} title={title} isPublished={isPublished}/>
})}
</div>
</div>)
}
小结:
- 如何定义和使用组件
- props-父组件给子组件传递数据
- 重构列表页 抽象出
QuestionCard
效果
children
场景: 当我们把内容签到在子组件标签中时,父组件会自动的在名为 children
的prop中接受内容
子组件传递父组件
顾名思义 其实就是子组件给父组件传递信息
tsx
function Son({onGetSonMsg}) {
// son 中的数据
const sonMsg = 'this is son msg';
return <div>this is son
<button onClick={() => onGetSonMsg(sonMsg)}>sendMsg</button>
</div>
}
function AppDemo() {
const [msg, setMsg] = useState('')
const getMsg = (msg) => {
console.log(msg)
// msg = '我是信息' 这么改是无效的
setMsg(msg)
}
return <div>
this is APP Son send msg =>{msg}
<Son onGetSonMsg={getMsg}/>
</div>
}
兄弟组件传递
使用状态提升实现兄弟组件通信
- 其实就是有共同父组件的两个子组件传递信息
- a 传递给父组件 然后由父组件 传递给 b
代码
tsx
import {useState} from "react";
function A({onGetAName}) {
const name = "a name"
return <div>this is A
<button onClick={() => onGetAName(name)}>send</button>
</div>
}
function B({pushAName}) {
return <div>this is B
{pushAName}
</div>
}
function AppDemo() {
const [aName, setAName] = useState('');
const getAName = (name) => {
console.log(name)
setAName(name)
}
return <div>
this is app
<A onGetAName={getAName}/>
<B pushAName={aName}/>
</div>
}
export default AppDemo;
function A({onGetAName}) {
const name = "a name"
return <div>this is A
<button onClick={() => onGetAName(name)}>send</button>
</div>
}
function B({pushAName}) {
return <div>this is B
{pushAName}
</div>
}
function AppDemo() {
const [aName, setAName] = useState('');
const getAName = (name) => {
console.log(name)
setAName(name)
}
return <div>
this is app
<A onGetAName={getAName}/>
<B pushAName={aName}/>
</div>
}
效果
HOOKS
useState
这是React 中的一个hook 函数 它允许我们向组件添加一个状态变脸,从而控制组件的渲染结果
tsx
const [msg, setMsg] = useState('')
- useState是一个函数 返回值是一个数组
- 数组中的第一个参数是状态变量,第二个参数是set函数用于修改状态
- useState的参数将作为状态变量的初始值
修改规则
在React 中 状态被认为是只读的 我们应该替换而不是修改 直接修改状态不会得到视图的更新
tsx
const [msg, setMsg] = useState('')
const getMsg = (msg) => {
console.log(msg)
// msg = '我是信息' 这么改是无效的
setMsg(msg)
}
//如果是对象作为参数
const [msg, setMsg] = useState({id:'122ds'})
const getMsg = (msg) => {
console.log(msg)
// msg = '我是信息' 这么改是无效的
setMsg({
...msg,
id:'123'})
}
useContext 组件通信
- 使用createContext 方法创建一个上下文对象 ctx=
- 在顶层组件 app 中 通过 ctx.Provider提供数据
- 在底层组件 通过 useContext钩子函数获取消费数据
案例 :
我们需要将app的消息传递到b
TSX
const MsgContext = createContext()
function A() {
return <div>this is A
<B/>
</div>
}
function B() {
const msg = useContext(MsgContext)
return <div>this is B from APP:{msg}
</div>
}
function AppDemo() {
const msg = "this is app msg"
return (<div>
<MsgContext.Provider value={msg}>
this is app
<A/>
</MsgContext.Provider>
</div>)
}
useEffect
这是React中的一个 hook 函数 ,用于在React 中创建不是由事件引起而是由渲染本身引起的操作,比如发送 AJAX请求 更改DOM等
基础使用
需求: 在组件渲染完毕后,从服务器获得列表数据展示
语法:
TSX
useEffect(()=>{},[])
- 参数1是一个函数,可以把它叫做副作用函数,函数内部可以放置要执行的操作
- 参数2是一个数组 ,数组里放置依赖项,不同依赖项会影响第一个参数的执行,当该参数是一个空数组的时候,副作用函数只会在组件渲染完毕后执行一次
tsx
import {useEffect, useState} from "react";
const URL = 'http://geek.itheima.net/v1_0/channels'
function AppDemo() {
const [list, setList] = useState([]);
useEffect(() => {
async function getList() {
const res = await fetch(URL)
const jsonRes = await res.json()
console.log(jsonRes)
setList(jsonRes.data.channels)
}
getList()
console.log("list", list)
}, []);
return (<div>
this is app
<ul>
{list.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>)
}
export default AppDemo;
效果
依赖项参数
TSX
function AppDemo() {
/*1. 没有依赖项*/
const [count, setCount] = useState(0);
// useEffect(() => {
// console.log("副作用函数执行了")
// });
/*2 传入空数组依赖*/
// useEffect(() => {
// console.log("副作用函数执行了")
// }, []);
useEffect(() => {
console.log("副作用函数执行了")
}, [count]);
return <div>this is app
<button onClick={() => setCount(count + 1)}>+{count}</button>
</div>
}
清除副作用
在useEffect
中编写的由渲染本身引起的对接组件外部的操作,社区也经常把它叫做副作用操作,我们想在组件卸载时把这个定时器清理掉,这个过程就是清理副作用
tsx
import {useEffect, useState} from "react";
function Son() {
useEffect(() => {
const timer = setInterval(() => {
console.log("定时器执行中...")
}, 1000)
return () => {
// 清楚副作用
clearInterval(timer)
}
}, []);
return <div>this is son</div>
}
function AppDemo() {
const [show, setShow] = useState(true)
return <div>this is app
{show && <Son/>}
<button onClick={() => setShow(false)}>卸载组件</button>
</div>
}
export default AppDemo;
自定义hook
暂时没有什么很好的例子 写一个比较简单的 之后再拓展
tsx
import {useState} from "react";
function useToggle() {
// 可复用代码
const [value, setValue] = useState(true);
const toggle = () => {
setValue(!value)
}
return {value, toggle}
}
function AppDemo() {
const {value, toggle} = useToggle()
return <div>this is app
{value && <div>this is show Toggle</div>}
<button onClick={toggle}>Toggle</button>
</div>
}
export default AppDemo;
效果
点击