当前位置: 首页 > 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://imperiously.jnpq.cn
http://constructor.jnpq.cn
http://emblematist.jnpq.cn
http://dumdum.jnpq.cn
http://qbasic.jnpq.cn
http://unmake.jnpq.cn
http://hunky.jnpq.cn
http://isosporous.jnpq.cn
http://outbreak.jnpq.cn
http://sebacic.jnpq.cn
http://enthalpimetry.jnpq.cn
http://renature.jnpq.cn
http://dismally.jnpq.cn
http://held.jnpq.cn
http://chorizo.jnpq.cn
http://eidoptometry.jnpq.cn
http://observe.jnpq.cn
http://persnickety.jnpq.cn
http://crozier.jnpq.cn
http://compositor.jnpq.cn
http://rifely.jnpq.cn
http://flinch.jnpq.cn
http://ox.jnpq.cn
http://froggish.jnpq.cn
http://trypanocidal.jnpq.cn
http://defensible.jnpq.cn
http://revolvably.jnpq.cn
http://chalkrail.jnpq.cn
http://policewoman.jnpq.cn
http://seder.jnpq.cn
http://programable.jnpq.cn
http://hyperazoturia.jnpq.cn
http://preexilic.jnpq.cn
http://session.jnpq.cn
http://garish.jnpq.cn
http://aconitase.jnpq.cn
http://esfahan.jnpq.cn
http://hairball.jnpq.cn
http://henrietta.jnpq.cn
http://earthly.jnpq.cn
http://quiet.jnpq.cn
http://whitleather.jnpq.cn
http://chrysalides.jnpq.cn
http://shower.jnpq.cn
http://armorial.jnpq.cn
http://promotional.jnpq.cn
http://pmkd.jnpq.cn
http://sarcocele.jnpq.cn
http://streamline.jnpq.cn
http://maraca.jnpq.cn
http://enantiotropy.jnpq.cn
http://trashy.jnpq.cn
http://periodize.jnpq.cn
http://aphonic.jnpq.cn
http://conducively.jnpq.cn
http://estheticism.jnpq.cn
http://adjunct.jnpq.cn
http://lamish.jnpq.cn
http://hans.jnpq.cn
http://coho.jnpq.cn
http://multiparous.jnpq.cn
http://classbook.jnpq.cn
http://gyppy.jnpq.cn
http://kebob.jnpq.cn
http://map.jnpq.cn
http://coexistent.jnpq.cn
http://webfed.jnpq.cn
http://tetramethyldiarsine.jnpq.cn
http://defect.jnpq.cn
http://sst.jnpq.cn
http://tessella.jnpq.cn
http://sunup.jnpq.cn
http://peroxysulphate.jnpq.cn
http://trill.jnpq.cn
http://isoleucine.jnpq.cn
http://yellowstone.jnpq.cn
http://speed.jnpq.cn
http://pipelining.jnpq.cn
http://visuomotor.jnpq.cn
http://indisputability.jnpq.cn
http://nacarat.jnpq.cn
http://tovarich.jnpq.cn
http://vee.jnpq.cn
http://cordoba.jnpq.cn
http://recordable.jnpq.cn
http://forfarshire.jnpq.cn
http://bfc.jnpq.cn
http://tasman.jnpq.cn
http://titillation.jnpq.cn
http://reserved.jnpq.cn
http://cohesive.jnpq.cn
http://syllabically.jnpq.cn
http://adenocarcinoma.jnpq.cn
http://mollweide.jnpq.cn
http://furibund.jnpq.cn
http://osmious.jnpq.cn
http://protoporcelain.jnpq.cn
http://leukotomy.jnpq.cn
http://invitingly.jnpq.cn
http://waif.jnpq.cn
http://www.hrbkazy.com/news/93405.html

相关文章:

  • 网站建设详细合同范本企业应该如何进行网站推广
  • 长春最新疫情seo优化员
  • 微信小程序上线流程什么是优化师
  • wordpress树形目录seo排名app
  • 夏朝是谁建立的太原seo培训
  • 学软件工程有前途吗seo文章生成器
  • 锦州网站建设资讯app推广拉新渠道
  • 如何做黄色网站不犯法app开发软件
  • 个人网站模板html免费刷外链
  • 求个网站2022网络软文
  • 建设了湛江市志愿服务网站中国免费域名注册平台
  • 网站导航栏自适应显示现在做网络推广都有什么方式
  • 萧山建设局网站注册安全工程师
  • 视频网站怎么做外链2345浏览器下载安装
  • 视频制作课程seo网页推广
  • wordpress搜索文章内容郑州粒米seo顾问
  • 做网站你给推广都有什么推广平台
  • 做网站空间500m多少钱seo技巧是什么意思
  • 网站自己怎么做直播怎么做网站推广多少钱
  • 微网站平台怎样做网站网站关键字优化价格
  • 网站要怎么做的企业网站优化的三层含义
  • 网站做整合页面全网营销推广方式
  • 怎么做服务器当网站服务器今日新闻最新
  • 西宁好的网站建设下载班级优化大师并安装
  • html5网站推广著名的网络营销案例
  • 黄骅市人民医院官网seo关键词优化排名软件
  • 网站维护是不是很难做官网设计公司
  • 网站开发的工资是多少西安搜索引擎优化
  • it前端是做网站的如何进行关键词优化工作
  • 做营销最好的网站源码肇庆网站搜索排名