ReactJs笔记摘录
文章目录
前言
以前2018年搞过一段时间react + antd开发,兜兜转转又回到react世界。本文力图记录一些易忘易混的知识点。
目录结构
├─package-lock.json
├─package.json ------------ // 项目配置
├─postcss.config.js ------- // postcss 配置
├─public ------------------ // 公开目录
│?├─favicon.ico
│?└─index.html
├─README.md ---------- // 项目说明
└─src --------------------- // 源码目录
├─App.tsx --------------- // 根组件
├─assets ---------------- // 静态资源目录
│?└─images -------------- // 图片目录
│? └─logo.png ------ // logo 图片
│?└─svg -------------- // 图标目录
│? └─logo.svg ------ // logo 图片
├─components ------------ // 公共组件目录
│?└─Menu ---------------- // 公共组件
│ ├─index.tsx ---------- // 组件文件
│ └─index.scss ------ // 组件样式
├─constants ---------------- // 项目常量
│?└─index.ts
├─libs ------------------ // 第三方库目录
├─index.tsx --------------- // 主入口
├─router ---------------- // 路由配置
│?└─index.tsx
├─store ----------------- // 状态管理
│?├─module.ts // 模块
│?└─index.ts // 入口
├─tests ----------------- // 测试目录
├─utils ----------------- // 工具目录
│?├─a.ts
│?└─index.ts
└─pages ----------------- // 视图目录
└─Home ---------------- // 页面组件
├─components -------- // 子组件目录
│?└─Header
│ ├─index.tsx
│ └─index.scss
└─index.tsx ---------- // 组件主体
组件
动态组件
实现方式一:
import React, {lazy} from 'react';
const Component = lazy(() => import(`../a`));
高阶组件
React高阶组件(HOC),是灵活使用react组件的一种技巧,高阶组件本身不是组件,它是一个参数为组件,返回值也是一个组件的函数。
Hook函数
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。它们可以帮助我们简化组件的代码并提高代码的可维护性。
Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则。react官方提供了一个 linter 插件来强制执行这些规则。
- 只在最顶层使用 Hook。不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。
- 只在 React 函数组件中调用 Hook,也可以在自定义 Hook 中调用其他 Hook。但不要在普通的 JavaScript 函数中调用 Hook。
以下是常见的 Hook 函数:
useState
useState 是一个用于在函数组件中添加状态的 Hook 函数。它接受一个初始状态值,并返回一个数组,其中第一个值为当前状态值,第二个值为更新状态的函数。
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useEffect
useEffect 是一个用于在函数组件中添加副作用的 Hook 函数。它接受一个回调函数和一个依赖数组作为参数。回调函数在组件挂载、更新或卸载时执行。依赖数组中的值发生变化时,也会重新执行回调函数。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
useContext
useContext 是一个用于在函数组件中使用 React Context 的 Hook 函数。它接受一个 Context 对象作为参数,并返回该上下文的当前值。
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function Example() {
return (
<ThemeContext.Provider value="dark">
<p>Current theme: {useContext(ThemeContext)}</p>
</ThemeContext.Provider>
);
}
useReducer
useReducer 是一个用于在函数组件中使用状态 reducer 的 Hook 函数。它接受一个 reducer 函数和一个初始状态值作为参数,并返回一个数组,其中第一个值为当前状态值,第二个值为更新状态的函数。
import React, { useReducer } from 'react';
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Current state: {state}</p>
</div>
);
}
function reducer(state, action) {
switch (action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
throw new Error();
}
}
useCallback
useCallback 是一个用于在函数组件中缓存函数的 Hook 函数。它接受一个函数作为参数,并返回一个缓存函数。如果传入的函数没有发生变化,则返回上次缓存的结果;否则,会重新创建一个新的缓存函数。
import React, { useState, useCallback } from 'react';
function Example() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
);
}
useMemo
useMemo 是一个用于在函数组件中缓存计算结果的 Hook 函数。它接受一个函数作为参数,并返回缓存的结果。如果传入的函数没有发生变化,则返回上次缓存的结果;否则,会重新计算新的结果。
import React, { useState, useMemo } from 'react';
function Example() {
const [count, setCount] = useState(0);
const doubleCount = useMem
JSX语法
根元素与斜杠
注意局部的jsx片段也要加根元素:
return (
<div>
{items.map((item) => (
// 此处只能有一个根元素!!!
<>
...
<div className="flex min-w-fit min-h-fit h-24 w-24 border-4 rounded-md bg-white-100 justify-center">
// img后要加斜杠
<img loading="lazy" src="http://www.icoolcms.com/cms/link/logo/2384"/>
</div>
</>
))}
</div>
)
使用变量
<img className="logoImg" loading="lazy" src={`http://www.icoolcms.com/cms/link/logo/${item.linkId}`}/>
推荐使用className替代class
className属性是React中使用的属性名称,用于避免与JavaScript的保留关键字class冲突。
属性写法
属性名称: React DOM 使用小驼峰命令来定义属性的名称,而不使用 HTML 属性名称的命名约定;
style 样式属性: 采用小驼峰命名属性的 JavaScript 对象。例如:
<img loading="lazy" src={`http://www.icoolcms.com/cms/link/logo/${item.linkId}`}
style={{width:'44px', height:'44px', borderRadius:'8px'}}/>
注意样式属性:backgroundColor和borderRadius,不能写成css里的background-color和border-radius
三元表达式 vs &&
这两种写法很常用:
<div>
Hello {name}
{isPro ? '?' : null}
{stars && (
<div>
Stars:{'☆'.repeat(stars)}
</div>
)}
</div>
antd和tailwindcss
可以在Antd组件中直接使用Tailwindcss类:
<Button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Click me
</Button>
组件通信
React 最推荐的数据交互方式是:props & onChnage。在这种交互方式里:对于一个可视组件 ComponentA,用 props 来向它发送信息,而用 onChange 回调函数来接收 ComponentA 发送的信息。在程序世界里,我们更喜欢把上述「交互方式」称为「接口」,虽然这个「接口」不是我们在面向对象语言里的 interface,但是跟 interface 有着类似的功能。
父传子:props和自定义函数事件
// 父组件
function Money() {
const [selected, setSelected] = useState('food');
return (
<TagsSection value={selected}/>
);
}
// 子组件
type Props = {
//声明props的数据类型
value: string;
}
const TagsSection: React.FC<Props> = (props) => {
const tagName = props.value;
return (
<div>the tag name is { tagName }</div>
);
};
子传父:props.onchange
import React, { useState } from "react";
const MyComponent = ({ onChange, initialValue }) => {
const [value, setValue] = useState(initialValue);
const handleChange = (event) => {
setValue(event.target.value);
onChange(event.target.value);
};
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
};
const App = () => {
const handleValueChange = (value) => {
console.log(`Value changed to: ${value}`);
};
return (
<MyComponent onChange={handleValueChange} initialValue="Initial value" />
);
};
export default App;
在这个示例中,我们定义了一个名为MyComponent的React组件,它接受一个onChange属性和一个initialValue属性。onChange属性定义了当组件的值发生变化时应该调用的函数,而initialValue属性则定义了组件的初始值。
在MyComponent组件内部,我们使用useState Hook来存储组件的值。然后,我们定义了一个handleChange函数,该函数在用户输入发生变化时被调用。在这个函数中,我们调用setValue来更新组件的值,并调用onChange函数来通知父组件值已更改。
最后,我们定义了一个App组件,该组件使用MyComponent组件,并将handleValueChange函数作为onChange属性的值传递。当组件的值发生变化时,handleValueChange函数将被调用,并输出新的值。
Context
Context 的使用方式:
- 使用 const xxx = createContext(null) 创建上下文
- 使用 <xxx.Provider> 圈定作用域
- 在作用域内使用 useContext(xxx) 来使用上下文
先创建一个 Context 对象:
import React from "react";
const ThemeContext = React.createContext();
export default ThemeContext;
再创建Provider:
import React from "react";
import ThemeContext from "./ThemeContext";
const ThemeProvider = ({ theme }) => {
return (
<ThemeContext.Provider value={theme}>
{/* 这里可以传递其他内容 */}
</ThemeContext.Provider>
);
};
export default ThemeProvider;
再创建Consumer, 它将使用 ThemeContext 上下文来获取 theme 数据
import React from "react";
import ThemeContext from "./ThemeContext";
const ThemeConsumer = ({ children }) => {
return (
<ThemeContext.Consumer>
{theme => {
// 在这里可以使用 theme 数据
return <>{children}</>;
}}
</ThemeContext.Consumer>
);
};
export default ThemeConsumer;
连起来:
import React from "react";
import ThemeProvider from "./ThemeProvider";
import ThemeConsumer from "./ThemeConsumer";
const theme = {
color: "blue",
};
const App = () => {
return (
<ThemeProvider theme={theme}>
<ThemeConsumer>
{({ theme }) => (
<div>
<h1>Theme color: {theme.color}</h1>
</div>
)}
</ThemeConsumer>
</ThemeProvider>
);
};
export default App;
在这个示例中,我们首先创建了一个 ThemeContext 对象,然后创建了一个 ThemeProvider 组件,它将提供 theme 数据。接下来,我们创建了一个 ThemeConsumer 组件,它将使用 ThemeContext 上下文来获取 theme 数据。最后,我们创建了一个 Root 组件,它将使用 ThemeProvider 组件提供 theme 数据,并使用 ThemeConsumer 组件来获取 theme 数据。
如果不用Consumer,也可以直接使用useContext()获得context数据:
// 使用useContext Hook
const { isLoggedIn, login, logout } = useContext(AuthContext);
状态管理
业内常规的成熟方案一般有:mobx、redux等专门的全局状态管理库,相对而言基本可以覆盖支持所有的复杂业务场景,再也就简单粗暴通过多个Context嵌套来实现共享。Redux 是借鉴 Flux 开发的,它们都是单向数据流,而 MobX 则有所不同,它是基于观察者模式实现。
React18以后的版本内置了useReducer() hook,它是 useState() 的替代品,简单的状态可以直接使用 useState,当我们遇到复杂多层级的状态或者下个状态要依赖上个状态时使用 useReducer() 则非常方便,在配合 Context 与 useContext() 就能实现类似 Redux 库的功能。
- 当我们项目中复杂程度较低时,建议只用state就可以了
- 如果仅仅因为存在多层传递数据的场景,不建议使用mobx或redux,可使用context解决
- 如果仅仅因为夸路由数据共享,不建议使用mobx或redux,可使用context或者路由传参解决
- 如果业务复杂,需要使用第三方状态管理解决复杂度,看下一条
- 当项目复杂度一般,小规模团队或开发周期较短、要求快速上线时,推荐使用mobx
- 当项目复杂度较高,团队规模较大或要求对事件分发处理可监控可回溯时,推荐使用redux,可尝试使用 rematch或@reduxjs/toolkit,减少模板代码
结合useContext()和useReducer()实现全局状态共享
写一个store.tsx:
import React, { createContext, useReducer, useContext } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
const StateContext = createContext();
const DispatchContext = createContext();
function useStateStore() {
return useContext(StateContext);
}
function useDispatchStore() {
return useContext(DispatchContext);
}
function StoreProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<StateContext.Provider value={state}>
<DispatchContext.Provider value={dispatch}>
{children}
</DispatchContext.Provider>
</StateContext.Provider>
);
}
export { useStateStore, useDispatchStore, StoreProvider };
修改状态:
import React from 'react';
import { useDispatchStore } from './store';
function Producer() {
const dispatch = useDispatchStore();
console.log('header udpate');
return (
<>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
export default Producer;
消费状态:
import React from 'react';
import { useStateStore } from './store';
function Footer() {
const state = useStateStore();
console.log('footer udpate');
return (
<p>{state.count}</p>
);
}
export default Footer;
UI 问题
选择哪个UI框架?
除了大名鼎鼎的antd之外,其实还有很多选择。react-bootstrap也是堂堂正正,大部分UI组件都不错。daisyui也冉冉升起。
编程实践
- 命名:props、变量用小驼峰,组件的文件名用大驼峰
- 引号:JSX 属性中用双引号("),但是在js里用单引号(')
- 自闭合标签:在自闭和标签内空一格,如<Foo />,当没有子元素时,最好用自闭合标签
- 推荐用 ref callback 函数
useRef() 返回一个具有单个 current 属性 的 ref 对象,并初始化为你提供的初始值
// bad
<Foo
ref="myRef"
/>
// good
<Foo
ref={(ref) => { this.myRef = ref; }}
/>
- 用箭头函数关闭局部变量
- 构造函数里绑定事件处理函数
- 不要在 React 组件里使用下划线作为内部方法名前缀
- State 的更新可能是异步的
- Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素 。我们推荐使用 createRef API 的方式 或者 回调函数的方式使用 Refs ,而不是使用 this.refs.textInput 这种过时的方式访问 refs ,因为它存在一些 问题。
- 建议使用路由懒加载当前用户所需要的内容
const OtherComponent = React.lazy(() => import(‘./OtherComponent’));
参考链接
React基础文档
React使用
- use-context-selector: 通过选择需要的状态从而规避掉无关的状态改变时带来的渲染开销
- 如何优雅地使用React Context
UI框架
- Ant Design: 88k stars,可惜不是基于tailwindcss的。
- react-bootstrap
- daisyui: 27k stars
- material-ui
- shadcn
- hyperui
- flowbite
Build websites even faster with components on top of Tailwind CSS,Start developing with an open-source library of over 600+ UI components, sections, and pages built with the utility classes from Tailwind CSS and designed in Figma. - primefaces
- preline: Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
- 基于Tailwindcss的headlessui
- Evergreen Segment
- Blueprint React UI
- Material Design for Bootstrap v5 & v4: Top quality open-source UI Kits
- Grommet
- visx
- Chakra
- React-admin
About A frontend Framework for building data-driven applications running on top of REST/GraphQL APIs, using TypeScript, React and Material Design - Semantic UI React
- nextui: Beautiful, fast and modern React UI library.
- fluent2
- retool: 要收费
- React Redux
组件
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!