当前位置: 首页 > news >正文

深圳nft网站开发公司百度知道官网入口

深圳nft网站开发公司,百度知道官网入口,美女做爰直播在线网站,重庆做网站费用文章目录 一、项目起航:项目初始化与配置二、React 与 Hook 应用:实现项目列表三、TS 应用:JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…

文章目录

    • 一、项目起航:项目初始化与配置
    • 二、React 与 Hook 应用:实现项目列表
    • 三、TS 应用:JS神助攻 - 强类型
    • 四、JWT、用户认证与异步请求
    • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式
    • 六、用户体验优化 - 加载中和错误状态处理
    • 七、Hook,路由,与 URL 状态管理
    • 八、用户选择器与项目编辑功能
    • 九、深入React 状态管理与Redux机制
      • 1.useCallback应用,优化异步请求
      • 2.状态提升,组合组件与控制反转


学习内容来源:React + React Hook + TS 最佳实践-慕课网


相对原教程,我在学习开始时(2023.03)采用的是当前最新版本:

版本
react & react-dom^18.2.0
react-router & react-router-dom^6.11.2
antd^4.24.8
@commitlint/cli & @commitlint/config-conventional^17.4.4
eslint-config-prettier^8.6.0
husky^8.0.3
lint-staged^13.1.2
prettier2.8.4
json-server0.17.2
craco-less^2.0.0
@craco/craco^7.1.0
qs^6.11.0
dayjs^1.11.7
react-helmet^6.1.0
@types/react-helmet^6.1.6
react-query^6.1.0
@welldone-software/why-did-you-render^7.0.1
@emotion/react & @emotion/styled^11.10.6

具体配置、操作和内容会有差异,“坑”也会有所不同。。。


一、项目起航:项目初始化与配置

  • 一、项目起航:项目初始化与配置

二、React 与 Hook 应用:实现项目列表

  • 二、React 与 Hook 应用:实现项目列表

三、TS 应用:JS神助攻 - 强类型

  • 三、 TS 应用:JS神助攻 - 强类型

四、JWT、用户认证与异步请求

  • 四、 JWT、用户认证与异步请求(上)

  • 四、 JWT、用户认证与异步请求(下)

五、CSS 其实很简单 - 用 CSS-in-JS 添加样式

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(上)

  • 五、CSS 其实很简单 - 用 CSS-in-JS 添加样式(下)

六、用户体验优化 - 加载中和错误状态处理

  • 六、用户体验优化 - 加载中和错误状态处理(上)

  • 六、用户体验优化 - 加载中和错误状态处理(中)

  • 六、用户体验优化 - 加载中和错误状态处理(下)

七、Hook,路由,与 URL 状态管理

  • 七、Hook,路由,与 URL 状态管理(上)

  • 七、Hook,路由,与 URL 状态管理(中)

  • 七、Hook,路由,与 URL 状态管理(下)

八、用户选择器与项目编辑功能

  • 八、用户选择器与项目编辑功能(上)

  • 八、用户选择器与项目编辑功能(下)

九、深入React 状态管理与Redux机制

1.useCallback应用,优化异步请求

当前项目中使用 useAsync 进行异步请求,但是其中有一个隐藏 bug,若是在页面中发起一个请求,这个请求需要较长时间3s(可以使用开发控制台设置请求最短时间来预设场景),在这个时间段内,退出登录,此时就会有报错:

Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

原因是虽然退出登录,组件销毁,但是异步函数还在执行,当它执行完进行下一步操作 setXXX 或是 更新组件都找不到对应已销毁的组件。

接下来解决一下这个问题。

编辑 src\utils\index.ts

...
/*** 返回组件的挂载状态,如果还没有挂载或者已经卸载,返回 false; 反之,返回 true;*/
export const useMountedRef = () => {const mountedRef = useRef(false)useEffect(() => {mountedRef.current = truereturn () => {mountedRef.current = false}}, [])return mountedRef
}

src\utils\use-async.ts 上应用:

...
import { useMountedRef } from "utils";
...
export const useAsync = <D>(...) => {...const mountedRef = useMountedRef()...const run = (...) => {...return promise.then((data) => {if(mountedRef.current)setData(data);return data;}).catch((error) => {...});};...
};

还有个遗留问题,在 useEffect 中使用的变量若是没有在依赖数组中添加就会报错,添加上又会造成死循环,因此之前用 eslint-disable-next-line 解决

// eslint-disable-next-line react-hooks/exhaustive-deps

现在换个方案,使用 useMemo 当然可以解决,这里推荐使用特殊版本的 useMemo, useCallback

修改 src\utils\use-async.ts

import { useCallback, useState } from "react";
...export const useAsync = <D>(...) => {...const setData = useCallback((data: D) =>setState({data,stat: "success",error: null,}), [])const setError = useCallback((error: Error) =>setState({error,stat: "error",data: null,}), [])// run 来触发异步请求const run = useCallback((...) => {...}, [config.throwOnError, mountedRef, setData, state, setError],)...
};

可以按照提示配置依赖:React Hook useCallback has missing dependencies: 'config.throwOnError', 'mountedRef', 'setData', and 'state'. Either include them or remove the dependency array. You can also do a functional update 'setState(s => ...)' if you only need 'state' in the 'setState' call.e

尽管如此,但还是难免会出现,在 useCallback 中改变 依赖值的行为,比如依赖值 XXX 对应的 setXXX,这时需要用到 setXXX 的函数用法(这样也可以省去一个依赖):

继续修改 src\utils\use-async.ts

...
export const useAsync = <D>(...) => {...const run = useCallback((...) => {...setState(prevState => ({ ...prevState, stat: "loading" }));...}, [config.throwOnError, mountedRef, setData, setError],)...
};

修改 src\utils\project.ts

...
import { useCallback, useEffect } from "react";
...export const useProjects = (...) => {...const fetchProject = useCallback(() =>client("projects", { data: cleanObject(param || {})}), [client, param])useEffect(() => {run(fetchProject(), { rerun: fetchProject });}, [param, fetchProject, run]);...
};
...

修改 src\utils\http.ts

...
import { useCallback } from "react";
...
export const useHttp = () => {...return useCallback((...[funcPath, customConfig]: Parameters<typeof http>) =>http(funcPath, { ...customConfig, token: user?.token }), [user?.token]);
};

总结:非状态类型需要作为依赖 就要将其使用 useMemo 或者 useCallback 包裹(依赖细化 + 新旧关联),常见于 Custom Hook 中函数类型数据的返回

2.状态提升,组合组件与控制反转

接下来定制化一个项目编辑模态框(编辑+新建项目),PageHeader hover 后可以打开(新建),ProjectList 中可以打开模态框(新建),里面的 List 的每行也可以打开模态框(编辑)

src\components\lib.tsx 中新增 padding0Button

...
export const ButtonNoPadding = styled(Button)`padding: 0;
`

新建 src\screens\ProjectList\components\ProjectModal.tsx(模态框):

import { Button, Drawer } from "antd"export const ProjectModal = ({isOpen, onClose}: { isOpen: boolean, onClose: () => void }) => {return <Drawer onClose={onClose} open={isOpen} width="100%"><h1>Project Modal</h1><Button onClick={onClose}>关闭</Button></Drawer>
}

新建 src\screens\ProjectList\components\ProjectPopover.tsx

import styled from "@emotion/styled"
import { Divider, List, Popover, Typography } from "antd"
import { ButtonNoPadding } from "components/lib"
import { useProjects } from "utils/project"export const ProjectPopover = ({ setIsOpen }: { setIsOpen: (isOpen: boolean) => void }) => {const { data: projects } = useProjects()const starProjects = projects?.filter(i => i.star)const content = <ContentContainer><Typography.Text type="secondary">收藏项目</Typography.Text><List>  {starProjects?.map(project => <List.Item><List.Item.Meta title={project.name}/></List.Item>)}</List><Divider/><ButtonNoPadding type='link' onClick={() => setIsOpen(true)}>创建项目</ButtonNoPadding></ContentContainer>return <Popover placement="bottom" content={content}>项目</Popover>
}const ContentContainer = styled.div`width: 30rem;
`

编辑 src\authenticated-app.tsx(引入 ButtonNoPaddingProjectPopoverProjectModal 自定义组件,并将模态框的状态管理方法传到对应组件 PageHeaderProjectList,注意接收方要定义好类型):

...
import { ButtonNoPadding, Row } from "components/lib";
...
import { ProjectModal } from "screens/ProjectList/components/ProjectModal";
import { useState } from "react";
import { ProjectPopover } from "screens/ProjectList/components/ProjectPopover";export const AuthenticatedApp = () => {const [isOpen, setIsOpen] = useState(false)...return (<Container><PageHeader setIsOpen={setIsOpen}/><Main><Router><Routes><Route path="/projects" element={<ProjectList setIsOpen={setIsOpen}/>} />...</Routes></Router></Main><ProjectModal isOpen={isOpen} onClose={() => setIsOpen(false)}/></Container>);
};
const PageHeader = ({ setIsOpen }: { setIsOpen: (isOpen: boolean) => void }) => {...return (<Header between={true}><HeaderLeft gap={true}><ButtonNoPadding type="link" onClick={resetRoute}><SoftwareLogo width="18rem" color="rgb(38,132,255)" /></ButtonNoPadding><ProjectPopover setIsOpen={setIsOpen}/><span>用户</span></HeaderLeft><HeaderRight>...</HeaderRight></Header>);
};
...

由于涉及登录后多个组件会发起调用,因此 ProjectModal 组件需要放在 AuthenticatedAppContainer

编辑 src\screens\ProjectList\index.tsx(引入 模态框的状态管理方法):

...
import { Row, Typography } from "antd";
...
import { ButtonNoPadding } from "components/lib";export const ProjectList = ({ setIsOpen }: { setIsOpen: (isOpen: boolean) => void }) => {...return (<Container><Row justify='space-between'><h1>项目列表</h1><ButtonNoPadding type='link' onClick={() => setIsOpen(true)}>创建项目</ButtonNoPadding></Row>...<ListsetIsOpen={setIsOpen}{...}/></Container>);
};
...

编辑 src\screens\ProjectList\components\List.tsx(引入 模态框的状态管理方法):

import { Dropdown, MenuProps, Table, TableProps } from "antd";
...
import { ButtonNoPadding } from "components/lib";
...
interface ListProps extends TableProps<Project> {...setIsOpen: (isOpen: boolean) => void;
}export const List = ({ users, setIsOpen, ...props }: ListProps) => {...return (<Tablepagination={false}columns={[...{render: (text, project) => {const items: MenuProps["items"] = [{key: 'edit',label: "编辑",onClick: () => setIsOpen(true)},];return <Dropdown menu={{ items }}><ButtonNoPadding type="link" onClick={(e) => e.preventDefault()}>...</ButtonNoPadding></Dropdown>}}]}{...props}></Table>);
};

可以明显看到,这种方式的状态提升(prop drilling)若是间隔层数较多时(定义和使用相隔太远),不仅有“下钻”问题,而且耦合度太高

下面使用 组件组合(component composition)的方式解耦

组件组合(component composition) | Context – React

编辑 src\authenticated-app.tsx(将 绑定了模态框 打开方法的 ButtonNoPadding 作为属性传给需要用到的组件):

...
export const AuthenticatedApp = () => {...return (<Container><PageHeader projectButton={<ButtonNoPadding type="link" onClick={() => setIsOpen(true)}>创建项目</ButtonNoPadding>} /><Main><Router><Routes><Routepath="/projects"element={<ProjectList projectButton={<ButtonNoPadding type="link" onClick={() => setIsOpen(true)}>创建项目</ButtonNoPadding>} />}/>...</Routes></Router></Main>...</Container>);
};
const PageHeader = (props: { projectButton: JSX.Element }) => {...return (<Header between={true}><HeaderLeft gap={true}>...<ProjectPopover { ...props } />...</HeaderLeft><HeaderRight>...</HeaderRight></Header>);
};
...

编辑 src\screens\ProjectList\components\ProjectPopover.tsx(使用传入的属性组件代替之前的 绑定了模态框 打开方法的 ButtonNoPadding ):

...
export const ProjectPopover = ({ projectButton }: { projectButton: JSX.Element }) => {...const content = (<ContentContainer>...{ projectButton }</ContentContainer>);...
};
...

编辑 src\screens\ProjectList\index.tsx(使用传入的属性组件代替之前的 绑定了模态框 打开方法的 ButtonNoPadding 并继续“下钻”):

...
export const ProjectList = ({ projectButton }: { projectButton: JSX.Element }) => {...return (<Container><Row justify="space-between">...{ projectButton }</Row>...<ListprojectButton={projectButton}{...}/></Container>);
};
...

编辑 src\screens\ProjectList\components\List.tsx(使用传入的属性组件代替之前的 绑定了模态框 打开方法的 ButtonNoPadding ):

...
interface ListProps extends TableProps<Project> {...projectButton: JSX.Element
}// type PropsType = Omit<ListProps, 'users'>
export const List = ({ users, ...props }: ListProps) => {...return (<Tablepagination={false}columns={[...{render: (text, project) => {return (<Dropdown dropdownRender={() => props.projectButton}><ButtonNoPaddingtype="link"onClick={(e) => e.preventDefault()}>...</ButtonNoPadding></Dropdown>);},},]}{...props}></Table>);
};
  • 编辑按钮这里使用并不恰当,不过这不是最终解决方案,理解思路即可
  • 浅析控制反转 - 知乎

部分引用笔记还在草稿阶段,敬请期待。。。

http://www.mmbaike.com/news/69570.html

相关文章:

  • 经营性网站备案信息查询沈阳百度推广优化
  • 怎么自己用手机做网站seo常用的工具
  • 网站你懂我意思正能量晚上不用下载直接进入seo网站搭建是什么
  • 个人网站官网网络营销的主要特点有哪些
  • 织梦网站底部黑链百度推广软件
  • flash网站设计欣赏代运营哪家公司最靠谱
  • 给自己家的公司做网站好做吗seo网站关键词排名快速
  • 郑州网站制作_郑州网页制作_做网站设计_河南网站制作网磁力搜索器kitty
  • 杭州微网站建设免费网站优化排名
  • 外贸网站的特色如何进行网络推广营销
  • 小说网站怎么推广南昌seo排名收费
  • 内容转载的网站怎么做西安seo高手
  • 做外贸网站需要多少钱怎么做推广
  • 网站信息化建设建议书seo综合优化公司
  • 自己做的网站怎么发到网上长沙建站工作室
  • 山东网站备案拍照重庆高端品牌网站建设
  • 钟表珠宝商城网站建设网站如何在百度刷排名
  • 做网站头片的高清图友情网
  • web实用网站开发实验报告企业qq官方下载
  • 网站建设有几种方式镇江seo快速排名
  • photoshop怎么做网站草图小吃培训
  • 网站规划名词解释网红推广一般怎么收费
  • 哪个网站做布料好自己怎么做引流推广
  • 无锡做网站优化公司网络营销课程个人总结
  • 企业做网站天津优化疫情政策
  • 龙华网站建设设计制作公司汕头seo排名收费
  • 女生做a视频的网站是什多少seo是什么公司
  • 可以做烟的网站吗seo教程论坛
  • 蚌埠集团网站建设中小企业管理培训班
  • 佛山模板网站建设做网页的网站