写在前面,在公司写了一年的Angular,现在准备转React,收拾收拾开干
环境搭建
create-react-app是一个快速创建React开发环境的工具,底层由Webpack构建,封装了配置细节,开箱即用 执行命令:
npx create-react-app react-basic
cd react-basic
npm start
1.JSX基础
概念:JSX是JavaScript和XMl(HTML)的缩写,表示在JS代码中编写HTML模版结构,它是React中构建UI的方式
JSX高频场景:
1.1 JS表达式
在JSX中可以通过 大括号语法{} 识别JavaScript中的表达式,比如常见的变量、函数调用、方法调用等等;
注意:if语句,switch语句,变量声明属于语句,不是表达式,不能出现在{}中
- 使用引号传递字符串
- 使用JS变量
- 函数调用和方法调用
- 使用JavaScript对象
const number = 100;
function getName(){
return 'Lily';
}
function App() {
return (
<div className="App">
{'this is a str'} {/*字符串识别*/}
{ number } {/*js变量识别*/}
{ getName() } {/*函数调用*/}
{ new Date().getDate() } {/*方法调用*/}
<div style={{color:'pink'}}> DIV </div> {/*使用JS对象*/}
</div>
);
}
1.2 列表渲染
在JSX中可以使用原生js中的map方法 实现列表渲染,注意加上key。
const list = [
{ id: 1001, name: 'Vue'},
{ id: 1002, name: 'React'},
{ id: 1003, name: 'Angular'}
]
function App() {
return (
<ul>
{list.map(item => <li key={item.id}>{item.id} {item.name}</li>)}
</ul>
);
}
1.3 条件渲染
语法:在React中,可以通过逻辑与运算符&& 三元表达式(?:) 实现基础的条件渲染
const isLogin = true;
{isLogin && <span>this a span</span>}
{isLogin ? <span>jack</span> : <span>loading...</span>}
1.4 复杂条件渲染
const type = 1 // 0|1|3
function getArticleJSX(){
if(type === 0){
return <div>无图模式模版</div>
}else if(type === 1){
return <div>单图模式模版</div>
}else(type === 3){
return <div>三图模式模版</div>
}
}
function App(){
return (
<div>
{/*调用函数渲染不同的模板*/}
{ getArticleJSX() }
</div>
)
}
2. React事件绑定
2.1 基础事件绑定
- 语法
on + 事件名称 = { 事件处理程序/回调函数 },整体上遵循驼峰命名法;记住 事件后面不要加括号() - 使用事件参数:在事件回调函数中设置形参e即可
function App(){
const clickHandler = (e)=>{
console.log('button按钮点击了', e)
}
return (
<button onClick={clickHandler}>click me</button>
)
}
2.2 传递参数
3.组件基础使用
3.1 组件基础使用
在React中,一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图UI, 渲染组件只需要把组件当成标签书写即可
// 1. 定义组件
function Button(){
return <button>click me</button>
}
// 2. 使用组件
function App(){
return (
<div>
{/* 自闭和 */}
<Button/>
{/* 成对标签 */}
<Button></Button>
</div>
)
}
4. 组件状态管理-useState
4.1 基础使用
//useState实现计数器
import { useState } from "react";
function App() {
//调用useState添加一个状态变量,count是状态变量,setCount是修改状态变量的方法
const [count, setCount] = useState(0)
//点击事件回调函数,用传入的新值修改count,重新使用新的count渲染UI
const handleClick = () => {
setCount(count+1)
}
return (
<div>
<button onClick={handleClick}>{count}</button>
</div>
);
}
4.2 状态的修改规则
在React中状态被认为是只读的,我们应该始终替换它而不是修改它, 直接修改状态不能引发视图更新
修改对象状态
对于对象类型的状态变量,应该始终给set方法一个全新的对象来进行修改
如果是数组,修改参数样例,先用中括号包起来,再用大括号包住替换的原始数组结构
const handlePublish = () => {
setCommentList(
[
...commentList,
{
rpid: 4,
user: {
uid: '30009257',
avatar,
uname: '黑马前端',
},
content: content,
ctime: '10-19 09:00',
like: 66,
}
]
)
}
5.组件的基础样式处理
行内样式/class类名控制
补充:
*案例:B站评论
1. 渲染评论列表(核心思路)
- 使用useState维护评论列表
- 使用map方法对列表数据进行遍历渲染(别忘记key)
2. 删除评论
需求:
- 只有自己的评论才显示删除按钮;
- 点击删除按钮,删除当前评论,列表中不再显示
核心思路:
- 删除显示-条件渲染
- 删除功能-拿到当前项id,以id为条件对评论列表做filter过滤
3. 渲染tab + 点击高亮实现
需求: 点击哪个tab,哪个做高亮处理
核心思路:
点击谁就把谁的 type(独一无二的标识)记录 下来,然后和遍历时的
每一项的type做匹配 ,谁匹配到就设置高亮的类名。
4. 排序功能实现
需求: 点击最新,评论列表按照列表创建时间倒序排列(新的在前),点击最热按照点赞数排序(多的在前)
核心思路: 把评论列表状态数据进行不同的排序处理,当成新值传给set函数重新渲染视图UI
5. 发表评论功能
需求: 输入框评论内容,获取评论内容,点击发布按钮发布评论
实现方式: rpid要求一个唯一的随机数id - uuid ctime要求以当前时间为标准,生成固定格式 - dayjs
6. 清空内容并重新聚焦
清空内容 - 把控制input框的value状态设置为空串 重新聚焦 - 拿到input的dom元素,调用focus方法
6. 表单数据双向绑定
6.1 受控绑定(推荐)
核心绑定流程 1.通过value属性绑定react状态 2.绑定onChange事件,通过事件参数e拿到输入框最新的值,反向修改到react状态
6.2 非受控绑定
7. 组件通信
7.1 父子组件通信
7.1.1 父传子
function Son(props){
return <div>{ props.name }</div>
}
function App(){
const name = 'this is app name'
return (
<div>
<Son name={name}/>
</div>
)
}
- props是一个对象,里面包含了父组件传递过来的所有数据
- 传递的是单向数据流,子组件无法修改父组件传递过来的数据
7.1.2 子传父
function Son({ onGetMsg }){
const sonMsg = 'this is son msg'
return (
<div>
{/* 在子组件中执行父组件传递过来的函数 */}
<button onClick={()=>onGetMsg(sonMsg)}>send</button>
</div>
)
}
function App(){
const [msg, setMsg] = useState('')
const getMsg = (msg) => {
setMsg(msg)
}
return (
<div>
<div> {msg}</div>
{/* 传递父组件中的函数到子组件 */}
<Son onGetMsg={ getMsg }/>
</div>
)
}
7.2 兄弟组件通信
7.3 跨层组件通信
// App -> A -> B
import { createContext, useContext } from "react"
// 1. createContext方法创建一个上下文对象
const MsgContext = createContext() //变量首字母大写,遵循驼峰
function A () {
return (
<div>
this is A component
<B />
</div>
)
}
function B () {
// 3. 在底层组件 通过useContext钩子函数使用数据
const msg = useContext(MsgContext)
return (
<div>
this is B compnent,{msg}
</div>
)
}
function App () {
const msg = 'this is app msg'
return (
<div>
{/* 2. 在顶层组件 通过Provider组件提供数据 */}
<MsgContext.Provider value={msg}>
this is App
<A />
</MsgContext.Provider>
</div>
)
}
export default App
8. useEffect
useEffect是一个React Hook函数,用于在React组件中创建不是由事件引起而是由渲染本身引起的操作(副作用), 比 如发送AJAX请求,更改DOM等等
说明:上面的组件中没有发生任何的用户事件,组件渲染完毕之后就需要和服务器要数据,整个过程属于“只由渲染引起的操作”
React的useEffect Hook和Vue的生命周期钩子函数有一些关联 useEffect 可以看作是以下几个生命周期函数的结合: componentDidMount\componentDidUpdtate\componentWillUnmount,这意味着useEffect可以在组件挂载mount\更新update\卸载unmount 时执行一些副作用。
9. 自定义Hook
概念:自定义Hook是以 use打头的函数,通过自定义Hook函数可以用来实现逻辑的封装和复用
// 封装自定义Hook
// 问题: 布尔切换的逻辑 当前组件耦合在一起的 不方便复用
import { useState } from "react"
function useToggle () {
// 可复用的逻辑代码
const [value, setValue] = useState(true)
const toggle = () => setValue(!value)
// 哪些状态和回调函数需要在其他组件中使用 return
return {
value,
toggle
}
}
// 封装自定义hook通用思路
// 1. 声明一个以use打头的函数
// 2. 在函数体内封装可复用的逻辑(只要是可复用的逻辑)
// 3. 把组件中用到的状态或者回调return出去(以对象或者数组)
// 4. 在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用
function App () {
const { value, toggle } = useToggle()
return (
<div>
{value && <div>this is div</div>}
<button onClick={toggle}>toggle</button>
</div>
)
}
10. 优化B站评论案例
优化需求 - 通过接口获取评论列表
- 使用
json-server工具模拟接口服务,通过axios发送请求 json-server是一个快速以.json文件作为数据源模拟接口服务的工具 axios是一个广泛使用的前端请求库 - 使用
useEffect调用接口获取数据
useEffect(() => {
//发送网络请求
},[])
优化需求-自定义Hook函数封装数据请求 一般思路: 编写一个use打头的函数 函数内部编写封装的逻辑 return出去组件中用到的状态和方法 组件中调用函数解构赋值使用
function useXXX (){
//状态逻辑
return {
//状态
//方法
}
}
优化需求-封装评论项item组件