React
基本语法
1. 在React中一个组件组件就是一个function,通过return的方式,返回对应的内容。jsx或者tsx是js 或 ts 结合 HTML的语法,React中的差值语法是放在{}中,Vue是放在{{}}中。
function App() {
const divContent = '标签内容'
const divTitle = '标签标题'
return (
// 或者使用这种方式,就类似于Vue中template
<>
<div className="App">
{/*只能返回一个根div*/}
wangxing
</div>
</>
);
}
-
基本的条件判断。
function App() {
let divContainer :any = null;
const flag = true
if(flag){
divContainer = <span>我是span</span>
}else{
divContainer = <p>我是p</p>
}
return (
// 或者使用这种方式,就类似于Vue中template
<div className="App" >
{divContainer}
</div>
);
}
-
与js结合:Map是有返回值的,所以使用map,结合html语法,非常方便的就实现了对应的功能。
function App() {
const list = [
{
id:1001,
name:'wangxing'
},
{
id:1002,
name: 'lv'
}
]
const divContent = list.map(item => <li key={item.id}>{item.name}</li>)
return (
// 或者使用这种方式,就类似于Vue中template
<div className="App" >
{divContent}
</div>
);
}
-
Fragment
比如这里的map方法中,想要一次映射两个li,首先想到的可能是<></>,但是这个标签不能放对应的属性,比如key,可以使用react提供的一个Fragment标签,这个可以解决不能放属性的问题,像template。
import {Fragment} from "react";
function App() {
const list = [
{
id:1001,
name:'wangxing'
},
{
id:1002,
name: 'lv'
}
]
const divContent = list.map(item =>
<Fragment key={item.id}>
<li>{item.name}</li>
<li>------------------------</li>
</Fragment>
)
return (
// 或者使用这种方式,就类似于Vue中template
<div className="App" >
{divContent}
</div>
);
}
-
useState
const [data,setData] = useState(data) ,是用setData的时候可以先展开原来的对象,在放上先要修改的属性,注意这里的setData中可以放上{},也可以放[],毕竟这两个都是对象。
const [data,setData] = useState([
{
id:1,
name:'wangxing'
},
{
id:2,
name:'xiaoming'
},
{
id:3,
name:'xiaoli'
},
])
let id :number = 3
const dataList = data .map(
item => <li key={item.id}>{item.name}</li>
)
function handleClick(e:any):void {
//是直接换掉整个对象
setData([
...data,
{
id: ++id,
name :'heieh'
}
])
}
这...data和{}放的顺序不同,渲染出来的列表也不同,因为数组是有顺序的,数组的操作,决定的渲染的操作,react和ts的结合非常好。
function handleClick(e:any):void {
//是直接换掉整个对象
setData(
data.filter(item => item.id !== 2)
)
}
组件通讯和插槽
dom
属性展开
function App() {
const divData = {
className:"App-header",
style:{
backgroundColor:'blue',
width:'100%',
height:'100%'
}
}
return (
// 或者使用这种方式,就类似于Vue中template
<div className="App" >
<div {...divData}>
ddd
</div>
</div>
);
}
自定义组件
组件通讯
function Article({title,content}:any){
return (
<div className="article">
<h2>{title}</h2>
<span>{content}</span>
</div>
)
}
export default function App() {
return (
// 或者使用这种方式,就类似于Vue中template
<>
<Article
title="标签1"
content="内容1"
/>
<Article
title="标签2"
content="内容2"
/>
<Article
title="标签3"
content="内容3"
/>
</>
);
}
展开属性
function Detail(props:any){
return (
<>
<span>{props.content}</span>
<p>{props.active ? '展示' : '隐藏'}</p>
</>
)
}
function Article({title,detailData}:any){
console.log(title);
return (
<div className="article">
<h2>{title}</h2>
<Detail {...detailData}/>
</div>
)
}
export default function App() {
//请求过来的数据
const articleData = {
title:'标题',
detailData : {
content:'内容',
active :true
}
}
return (
// 或者使用这种方式,就类似于Vue中template
<>
<Article
{...articleData}
/>
</>
);
}
插槽
import {ReactNode} from "react";
function List({children}: {children: ReactNode}) {
return (
<ul>
{children}
</ul>
)
}
export default function App() {
return (
// 或者使用这种方式,就类似于Vue中template
<>
<List>
<li>列表项</li>
<li>列表项</li>
<li>列表项</li>
</List>
</>
);
}
如果出来插槽的内容外,还有别的属性怎么办???直接写上即可,因为在React中没有插槽这个概念,或者说他是一个特殊的props,放在了children中。
import {ReactNode} from "react";
function List({children,title = '默认标题',footer= <div>默认内容</div>}: {children: ReactNode,title?: string,footer?: ReactNode}) {
return (
<>
<div>
{title}
</div>
<ul>
{children}
</ul>
{footer}
</>
)
}
export default function App() {
return (
// 或者使用这种方式,就类似于Vue中template
<>
<List
title='list1'
footer={<span>底部内容1</span>}
>
<li>列表项1</li>
<li>列表项1</li>
<li>列表项1</li>
</List>
<List
title='list2'
footer={<span>底部内容2</span>}
>
<li>列表项2</li>
<li>列表项2</li>
<li>列表项2</li>
</List>
</>
);
}
子组件向父组件传值,和Vue的emit类似,父组件给子组件提供一个方法,子组件接收到,在适合的时候调用这个方法即可,不过也是通过Props来进行的。
import {ReactNode, useState} from "react";
function Detail({onActive}:any){
const [isShow,setShow] = useState(true)
function handleClick(){
setShow(!isShow)
onActive(isShow)
}
return (
<div>
<p
style={{display: isShow ? "block" : "none"}}
>
Detail的内容
</p>
<button onClick={handleClick}>
按钮
</button>
</div>
)
}
export default function App() {
function handleActive(status:boolean){
console.log('当前组件的状态:',status)
}
return (
// 或者使用这种方式,就类似于Vue中template
<>
<Detail
onActive={handleActive}
/>
</>
);
}
useContext
使用Context完成不同层级数据的渲染。
import React, {useContext} from "react";
function Select({children}: any) {
const level :number = useContext(LevelContext);
return (
<section className="selection">
<LevelContext.Provider value={level + 1}>
{children}
</LevelContext.Provider>
</section>
)
}
function Heading({children}: any) {
const level :number = useContext(LevelContext);
switch (level) {
case 1:
return <h1>{children}</h1>
case 2:
return <h2>{children}</h2>
case 3:
return <h3>{children}</h3>
case 4:
return <h4>{children}</h4>
default:
throw Error(`Unknown level: ${level}: ${JSON.stringify(children)}`)
}
}
const LevelContext = React.createContext<number>(1)
export default function App(){
return (
<div>
<Select>
<Heading >
<Select>
<Heading >Title</Heading>
<Heading >Title</Heading>
<Heading >Title</Heading>
</Select>
</Heading>
<Heading >Title</Heading>
<Heading >Title</Heading>
<Heading >
<Select>
<Heading >Title</Heading>
<Heading >Title</Heading>
<Heading >
<Select>
<Heading >Title</Heading>
</Select>
</Heading>
</Select>
</Heading>
<Heading >Title</Heading>
<Heading >Title</Heading>
</Select>
</div>
)
}
React Hooks
Reducer
统一管理状态的操作方式
import {useReducer, useState} from "react";
function countReducer(state:any,action:{type:string}){
switch(action.type){
case "Increment":
return state + 1;
case "Decrement":
return state - 1;
default:
throw new Error(`Unknown action type ${action.type}`);
}
}
export default function App (){
const [state, dispatch] = useReducer(countReducer, 0)
const handleIncrement = ()=> dispatch({type:"Increment"})
const handleDecrement = ()=> dispatch({type:"Decrement"})
return (
<div className="App">
<button onClick={handleIncrement}>+</button>
<span>{state}</span>
<button onClick={handleDecrement}>-</button>
</div>
)
}
useRef
useRef中的值不会随着组件的更新而发生改变。
import {useRef, useState} from "react";
export default function App(){
const [count,setCount] = useState(0)
const preCount:React.MutableRefObject<number | undefined> = useRef()
function handleClick(){
preCount.current = count
setCount(count + 1)
}
return (
<div>
<p>最新的count:{count}</p>
<p>上次的count:{preCount.current}</p>
<button onClick={handleClick}>增大Count</button>
</div>
)
}
这里的就和Vue中的ref很像,可以拿到对应的组件。
import {useRef, useState} from "react";
export default function App(){
const inputRef:any = useRef(null)
function handleClick() {
inputRef.current.focus()
}
return (
<div>
<input type={'text'} ref={inputRef}/>
<button onClick={handleClick}>click</button>
</div>
)
}
访问子组件内部功能
import {forwardRef, useImperativeHandle, useInsertionEffect, useRef, useState} from "react";
const Child = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
// 暴露给父组件的方法
handleClick() {
console.log('子组件的方法被调用了')
}
}))
return (
<div>子组件</div>
)
})
export default function App(){
const childRef:any = useRef(null)
function handleClick() {
childRef.current.handleClick()
}
return (
<div>
<Child ref={childRef} />
<button onClick={handleClick}>click</button>
</div>
)
}
useEffect
副作用函数
import {useEffect, useReducer, useState} from "react";
function countReducer(state:any,action:{type:string}){
switch(action.type){
case "Increment":
return state + 1;
case "Decrement":
return state - 1;
default:
throw new Error(`Unknown action type ${action.type}`);
}
}
export default function App (){
const [state, dispatch] = useReducer(countReducer, 0)
const handleIncrement = ()=> dispatch({type:"Increment"})
const handleDecrement = ()=> dispatch({type:"Decrement"})
useEffect(()=>{
console.log("state changed",state)
},[]) // 第二个参数是依赖项,当依赖项发生变化时,useEffect才会执行
return (
<div className="App">
<button onClick={handleIncrement}>+</button>
<span>{state}</span>
<button onClick={handleDecrement}>-</button>
</div>
)
}
useMemo 和 useCallBack
父组件重新渲染,会有导致子组件也进行重新渲染的问题。当父组件刷新的时候,会进行相应的判断,比如父传子的props有没有发生改变?如果没有发生改变,即使父组件重新渲染,子组件也不会重新渲染,这个时候只要传给子组件的数据不发生改变即可。
useMemo(用于状态)
import React, {useState} from "react";
function Child({value}:any) {
function computeValue(){
console.log(value)
return <span>{value}</span>
}
return computeValue()
}
export default function App() {
const [value,setValue] = useState(0)
const [content,setContent] = useState("wangxing")
function handleContent(){
setContent(content + content)
}
return (
<div className="App">
<div>
{content}
</div>
<button onClick={handleContent}>改变内容</button>
<Child value={value} />
<button onClick={()=>setValue(value+1)}>增加数据</button>
</div>
)
}
这个例子来说,如果不修改count,修改别的内容,触碰到页面刷新了,也会调用computeValue函数。
import React, {useMemo, useState} from "react";
function Child({value}:any) {
const computeValue = useMemo(() =>{
console.log(value)
return <span>{value}</span>
},[value])
return computeValue
}
export default function App() {
const [value,setValue] = useState(0)
const [content,setContent] = useState("wangxing")
function handleContent(){
setContent(content + content)
}
return (
<div className="App">
<div>
{content}
</div>
<button onClick={handleContent}>改变内容</button>
<Child value={value} />
<button onClick={()=>setValue(value+1)}>增加数据</button>
</div>
)
}
这种方式就可以解决问题。
useCallback(用于方法)
import React, {useMemo, useState} from "react";
function Child({onClick}:any) {
console.log("Child渲染")
const handleClick = () => {
onClick()
}
return <>
<button onClick={handleClick}>子组件</button>
</>
}
export default function App() {
const [content,setContent] = useState("wangxing")
function handleContent(){
setContent(content + content)
}
const handleData = () => {
console.log("handleData----father")
}
return (
<div className="App">
<div>
{content}
</div>
<button onClick={handleContent}>改变内容</button>
<Child onClick={handleData} />
</div>
)
}
上面的代码,点击了改变内容的按钮(父组件中的),父组件重新渲染,也会导致Child组件进行重新渲染,使用React的memo()可以把子组件变成一个记忆组件,只要确保父给子的函数不会发生变化,子组件就不会有重新渲染的问题,问题的关键就变成如何确保父组件重新渲染,对应的函数也不发生改变----> useCallBack.
import React, {useMemo, memo, useState, useCallback} from "react";
const Child = memo(function ({onClick}:any) {
console.log("Child渲染")
const handleClick = () => {
onClick()
}
return <>
<button onClick={handleClick}>子组件</button>
</>
})
export default function App() {
const [content,setContent] = useState("wangxing")
function handleContent(){
setContent(content + content)
}
const handleData = useCallback(() => {
console.log("handleData----father")
},[])// [] 是一个依赖数组,如果依赖数组为空,则不会重新渲染
return (
<div className="App">
<div>
{content}
</div>
<button onClick={handleContent}>改变内容</button>
<Child onClick={handleData} />
</div>
)
}
上面的代码做了两点:
-
Child变成记忆组件
-
handleData方法使用useCallback包裹,使其不会发生改变。
笔记内容的视频参考:
40分钟学会React18组件通信与插槽 可能是你学会React最好的机会 前端开发必会框架 无废话精品视频_哔哩哔哩_bilibili
30分钟学会React18核心语法 可能是你学会React最好的机会 前端开发必会框架 无废话精品视频_哔哩哔哩_bilibili20分钟学会React Hooks 前端开发必看 AI编程工具 CodeGeeX 体验_哔哩哔哩_bilibili