1、Hook 使用规则
Hook 就是JavaScript 函数,但是使用它们会有两个额外的规则:
1、只能在函数最外层调用 Hook。不要在循环、条件判断或者嵌套函数(子函数)中调用。
2、只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
3、在多个useState()调用中,渲染之间的调用顺序必须相同。
React 支持 class 和 function 两种形式的组件,class 支持 state 属性和生命周期方法,而 function 组件也通过 hooks api 实现了类似的功能。
fiber 架构是 React 在 16 以后引入的,之前是 jsx -> render function -> vdom 然后直接递归渲染 vdom,现在则是多了一步 vdom 转 fiber 的 reconcile,在 reconcile 的过程中创建 dom 和做 diff 并打上增删改的 effectTag,然后一次性 commit。这个 reconcile 是可被打断的,可以调度,也就是 fiber 的 schedule。
hooks 的实现就是基于 fiber 的,会在 fiber 节点上放一个链表,每个节点的 memorizedState 属性上存放了对应的数据,然后不同的 hooks api 使用对应的数据来完成不同的功能。
链表自然有个创建阶段,也就是 mountXxx,之后就不需要再 mount 了,只需要 update。所以每个 useXx 的实现其实都是分为了 mountXxx 和 updateXxx 两部分的。
我们看了几个简单的 hooks: useRef、useCallback、useMemo,它们只是对值做了缓存,逻辑比较纯粹,没有依赖 React 的调度。而 useState 会触发 fiber 的 schedule,useEffect 也有自己的调度逻辑。实现上相对复杂一些,我们没有继续深入。
其实给我们一个对象来存取数据,实现 useRef、useCallback、useMemo 等 hooks 还是很简单的。对于需要调度的,则复杂一些。
对于自定义的 hooks,那个就是个函数调用,没有任何区别。(lint 的规则不想遵守可以忽略)
所有 hooks api 都是基于 fiber 节点上的 memorizedState 链表来存取数据并完成各自的逻辑的。
所以,hooks 的原理简单么?只能说有的简单,有的不简单。
1、操作当前DOM
2、保持数据持久性
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列
useMemo和useCallback都是reactHook提供的两个API,用于缓存数据,优化性能;两者接收的参数都是一样的,第一个参数表示一个回调函数,第二个表示依赖的数据
useMemo 缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态
useCallback 缓存的结果是函数,主要用于缓存函数,应用场景如需要缓存的函数,因为函数式组件每次任何一个state发生变化,会触发整个组件更新,一些函数是没有必要更新的,此时就应该缓存起来,提高性能,减少对资源的浪费;另外还需要注意的是,useCallback应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。
该函数有两个参数:
1.函数,useCallBack会固定该函数的引用,只要依赖项没有发生改变,则始终返回之前函数的地址
2.数组,记录依赖项
(类似于useEffect)
该函数返回:引用相对固定的函数地址
怎么写和使用
优缺点
React-redux是为方便react使用,在redux上封装的库。在实际的项目中我们可以使用redux也可以直接使用react-redux。
store dispatch aciton
最近在研究 redux 的一些用法,目前在 react中使用 redux 的话,基本分为两种用法:
使用 redux,这个是非官方团队在维护的,而且不单在 react项目中可以使用,在 vue 的项目中也可以使用
使用 react-redux,这个是 Facebook 官方在维护,不过其中的一些 API 相对来说有点难理解
React 官方团队目前针对 react 项目中的状态管理,比较推荐的做法是使用 @reduxjs/toolkit + react-redux 这种方式,这种方式简化了之前的一些操作,特别是 action type 上,当然相对地,也需要了解一下 toolkit 中的一些概念和 API,弄清楚后就会发现,使用 toolkit 来操作 redux 会方便很多
createSelector、createSlice、PayloadAction、createAsyncThunk、combineReducers、createListenerMiddleware、CreateSliceOptions、
isRejected、SliceCaseReducers、StoreEnhancer、Reducer、AnyAction、combineReducers、 compose、createStore(已经废弃)
Redux Toolkit 它最初是为了帮助解决有关 Redux 的三个常见问题而创建的:
Type definitions — You can use it easily with TypeScript.
类型定义 -您可以在TypeScript中轻松使用它。
Mutate state directly — You no longer need to make a copy of state or to spread state. RTK comes with Immer which allows you to mutate state directly in the code while Immer does all the heavy lifting under the hood.
直接更改状态 -您不再需要复制状态或传播状态。 RTK随Immer一起提供 ,它允许您直接在代码中更改状态,而Immer则在后台进行所有繁重的工作。
Redux Thunk — Writing async code in regular Redux requires installing additional middleware like Redux Saga or Redux Thunk. Not anymore. RTK comes bundled with Redux Thunk for all that async beauty.
Redux Thunk-在常规Redux中编写异步代码需要安装其他中间件,例如Redux Saga或Redux Thunk。 不再。 RTK与Redux Thunk捆绑在一起,提供了所有异步美感。
One file — There is one file for all your reducers, actions, and action creators.
一个文件 —所有减速器,动作和动作创建者都有一个文件。
Redux Dev Tools — I can’t tell you how many times using Redux DevTools (you need to install the extension in your browser to use it) has helped me debug code faster and stopped me from having days where I want to smash my keyboard (no, I’d never do that — hopefully). RTK comes with Redux DevTools, which means you would never need to add any additional middleware to use it.
Redux Dev Tools-我无法告诉您使用Redux DevTools的次数 (您需要在浏览器中安装扩展程序才能使用它)帮助我更快地调试代码,并避免了我想要砸键盘的日子(不,我永远不会那样做-希望如此)。 RTK随附Redux DevTools,这意味着您无需添加任何其他中间件即可使用它。
React 实际上只是 UI 框架,通过 JSX 生成动态 dom 渲染 UI,没有架构、没有模板、没有设计模式、没有路由、也没有数据管理。所以需要借助其他工具。
react-redux 提供了两个重要的对象,provider 和 connect,前者使 React 组件可被连接(connectable),后者把 React 组件和 Redux 的 store 真正连接起来。
React-Redux 提供Provider组件,可以让容器组件拿到state。
使组件层级中的 connect() 方法都能够获得 Redux store。正常情况下,你的根组件应该嵌套在 中才能使用 connect() 方法。
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
连接 React 组件与 Redux store。
连接操作不会改变原来的组件类。
反而返回一个新的已与 Redux store 连接的组件类。
1、useDispatch使用这个 hook 能得到 redux store 的 dispatch 方法引用,通常用于“手动” dispatch action
2、组件可以通过useSelector访问store中释放的state数据
3、useStore关于这个api,我也是没怎么用过,不过上网查了一波,就如字面上的意思,useStore() 这个 Hook 直接获取到了 Redux store 的引用,所以可以使用到更“底层”的API
React.memo() 和 PureComponent 很相似,它可以帮助我们控制什么时候重新渲染组件。
"react-dnd": "^14.0.2",
"react-dnd-html5-backend": "^14.0.0",
"react-draggable": "^4.4.3",
mport { DraggableCore, DraggableEventHandler } from 'react-draggable';
学习最新版本,版本向下兼容,react 15.X.X 版本是划分界限
Hooks在React 16.8版本中正式被引入。它可以让你在不用写class的条件下使用state以及其他的React特性。
如何使用
createStore:创建一个store仓库
store.dispatch():向stroe派发一个action
store.subscribe():监听state变化,一旦有变化重新setState
store.getState():获取当前状态
const reducer = (state = {count: 0}, action) => {
switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: return state;
}
}
const actions = {
increase: () => ({type: 'INCREASE'}),
decrease: () => ({type: 'DECREASE'})
}
//通过 reducer 创建一个 store
const store = createStore(reducer);
//每当我们在 store 上 dispatch 一个 action,store 内的数据就会相应地发生变化。
store.subscribe(() =>
console.log(store.getState())
);
store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}
https://blog.csdn.net/qq_43952245/article/details/103264581
2019 年 “年度突破”React 开源奖和“最有影响的贡献”JavaScript 开源奖的获得者
Immer(德语为:always)是一个小型包,可让您以更方便的方式使用不可变状态。
Immer 简化了不可变数据结构的处理
之前在项目中遇到redux的数据持久化问题,当页面刷新时,redux中存储的状态会重置,导致之前的操作无效,这要是用户手欠,不不不!是好奇,F5刷新下界面,问题可就大了,所以研究了一下redux数据持久化组件redux-persist
如果action是一个promise, 则会等待promise完成,将完成的结果作为action触发,如果action不是一个promise,则判断其payload是否是一个promise,如果是,等待promise完成,然后将其作为payload的值触发。
redux的中间件,为了处理异步问题
各自的优缺点
如何测试? 测试哪些东西? 与正常的测试有什么区别吗?
configureStore使用一个函数调用建立一个配置良好的 Redux 存储,包括组合 reducer,添加 thunk 中间件,以及设置 Redux DevTools 扩展。它也比 createStore 更容易配置,因为它接受命名选项参数。
createSlice让你可以编写使用immmer库的reducer来支持使用”可变“JS 语法,如state.value = 123,不需要任何扩展,它还自动为每个reducer生成action函数,并根据action在内部自动生成type类型字符串,更好的支持 typescript
react-router:实现了路由的核心功能
react-router-dom:基于react-router,加入了在浏览器运行环境下的一些功能,例如Link组件、BrowserRouter和HashRouter组件。
RR4 本次采用单代码仓库模型架构(monorepo),这意味者这个仓库里面有若干相互独立的包,分别是:
react-router React Router 核心
react-router-dom 用于 DOM 绑定的 React Router
react-router-native 用于 React Native 的 React Router
react-router-redux React Router 和 Redux 的集成
react-router-config 静态路由配置的小助手
引用
react-router 还是 react-router-dom?
在 React 的使用中,我们一般要引入两个包,react 和 react-dom,那么 react-router 和react-router-dom 是不是两个都要引用呢?
非也,坑就在这里。他们两个只要引用一个就行了,不同之处就是后者比前者多出了 这样的 DOM 类组件。
因此我们只需引用 react-router-dom 这个包就行了。当然,如果搭配 redux ,你还需要使用 react-router-redux。
react-router-v4,我称之为“第四代react-router”,react-router和react-router-dom的区别是什么呢?
为什么有时候我们看到如下的写法:
写法1:
import {Swtich, Route, Router, HashHistory, Link} from ‘react-router-dom’;
写法2:
import {Switch, Route, Router} from ‘react-router’;
import {HashHistory, Link} from ‘react-router-dom’;
先简单说下各自的功能:
react-router: 实现了路由的核心功能
react-router-dom: 基于react-router,加入了在浏览器运行环境下的一些功能,例如:Link组件,会渲染一个a标签,Link组件源码a标签行; BrowserRouter和HashRouter组件,前者使用pushState和popState事件构建路由,后者使用window.location.hash和hashchange事件构建路由。
react-router-native: 基于react-router,类似react-router-dom,加入了react-native运行环境下的一些功能。
从源码层面来说明:
首先看react-router-dom中的Switch组件的源码
// Written in this round about way for babel-transform-imports
import { Switch } from ‘react-router’
export default Switch
只是从react-router中导入Switch组件,然后重新导出而已。
查看其他模块的源码,
Route组件的源码
Router组件的源码
…
和Swtich一样,都是从react-router中导入了相应的组件,重新导出而已,并没有对实现做什么特殊处理。
结论:
react-router-dom依赖react-router,所以我们使用npm安装依赖的时候,只需要安装相应环境下的库即可,不用再显式安装react-router。
基于浏览器环境的开发,只需要安装react-router-dom;基于react-native环境的开发,只需要安装react-router-native。
npm会自动解析react-router-dom包中package.json的依赖并安装。
react-router-dom中package.json依赖:
“dependencies”: {
“history”: “^4.7.2”,
“invariant”: “^2.2.2”,
“loose-envify”: “^1.3.1”,
“prop-types”: “^15.5.4”,
“react-router”: “^4.2.0”,
“warning”: “^3.0.0”
}
安装了react-router-dom,npm会解析并安装上述依赖包。可以看到,其中包括react-router。
在单页应用中,一个web项目只有一个html页面,一旦页面加载完成之后,就不用因为用户的操作而进行页面的重新加载或者跳转,其特性如下:
改变 url 且不让浏览器像服务器发送请求
在不刷新页面的前提下动态改变浏览器地址栏中的URL地址
其中主要分成了两种模式:
hash 模式:在url后面加上#,如http://127.0.0.1:5500/home/#/page1
history 模式:允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录
url: hash带有#,history没有