问题描述
1、 我们知道JavaScript
中想要实现类似于Java
静态变量的话,可以用闭包和localStorage
实现。
闭包:有权访问另一个函数作用域变量的函数叫做闭包。
满足三个条件
1. 定义了局部变量
2. 函数返回一个匿名函数
3. 返回的匿名函数中需要使用第一步定义的局部变量
局部变量被另一个函数使用,即变量被引用,所以就不会被回收了,因此可以利用这一个特性来封装一个私有变量。
本地存储是一个window
的属性,包括localStorage
和sessionStorage
,前者一直存在,后者只是伴随着session
,窗口关闭就会消失。
localStorage
:存储数据就是直接给window.localStorage
添加一个属性,它的读写删除等操作以键值对的方式存在。
2、 因为JavaScript
中没有static
这一说法,React
中的Hook state
就相当于是静态变量
①执行setN
的时候会发生什么?n会发生什么变化?App()
会重复执行吗?
n会改变,App会重复执行
②如果App()重复执行,useState(0
)的时候,n的值会变化吗?
n每次的值会不一样,但是不是由setN改变的!
1、setN修改数据,并将更新后的数据存进state,覆盖原来的state
2、setN会触发<App />重复渲染(re-render)
3、useState会从更新后的state读取n的最新值
4、每个组件都拥有自己的state
useState调用顺序
若第一次渲染时n是第一个,m是第二个,k是第三个
则第二次渲染时必须保证顺序完全一致
所以React不允许出现如下代码
function App(){
const [n, setN] = React.useState(0)
let m, setM
if(n % 2 === 1){
/* error:
* React Hook "React.useState" is called conditionally.
* React Hooks must be called in the exact same order in every component render
*原因: 因为useState内部原理是把state声明成一个数组,需要顺序一一对应,如上图
*/
[m, setM] = React.useState(0)
}
return (
<div className='App'>
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
<p>{m}</p>
<p>
<button onClick={() => setM(m + 1)}>+1</button>
</p>
</div>
)
}
hook如何保存数据
在react中通过currentRenderingFiber
来标识当前渲染节点,每个组件都有一个对应的fiber
节点,用来保存组件的相关数据信息。
每次函数组件渲染时,currentRenderingFiber
就被赋值为当前组件对应的fiber
,所以实际上hook是通过currentRenderingFiber
来获取状态信息的。
currentRenderingFiber.memorizedState
中保存一条hook对应数据的单向链表。
const hookNum = {
memorizedState: null,
next: hookName
}
hookName.next = hookAge;
currentRenderingFiber.memorizedState.next = hookNum;
当函数组件渲染时,每执行到一个hook,就会将currentRenderingFiber.memorizedState
的指针向后移一下。这也是hook的调用顺序不能改变
的原因(不能再条件语句中使用hook)
手写useState
let _state //全局_state用来存储state的值,避免重新渲染的时候被myUseState重置为初始值
const myUseState = initialValue => {
_state = _state === undefined ? initialValue : _state
const setState = (newValue) => {
_state = newValue
render()
}
return [_state, setState]
}
const render = () => {
ReactDOM.render(<App1 />, document.getElementById('root'))
}
function App1(){
const [n, setN] = myUseState(0)
return (
<div className='App'>
<p>{n}</p>
<p>
<button onClick={() => setN(n + 1)}>+1</button>
</p>
</div>
)
}
3、Dva的model
如何存储数据?为什么可以跨页面使用?
Provider
Provider组件的任务是将stroe传递给子组件,它只是一个传递数据的组件,只需要将子组件展示出来就好。
本质上是个高阶组件,也是代理模式的一种实践方式。接收 redux 生成的 store 做参数后,通过上下文 context 将 store 传递进被代理组件。在保留原组件的功能不变的同时,增加了 store 的 dispatch 等方法。
(源自Dva官网)
在整个应用上包一层,使整个应用成为Provider的子组件
接收Redux的store作为props,通过context对象传递给子组件,所有的子组件都可以取得store
通过context传递属性的方式可以大量减少通过props 逐层传递属性的方式,可以减少组件之间的直接依赖关系。
connect
方法
connect 也是一个代理模式实现的高阶组件,为被代理的组件实现了从 context 中获得 store 的方法。
connect 方法返回的也是一个 React 组件,通常称为容器组件。因为它是原始 UI 组件的容器,即在外面包了一层 State。
connect 方法传入的第一个参数是 mapStateToProps 函数,mapStateToProps 函数会返回一个对象,用于建立 State 到 Props 的映射关系。
(源自Dva官网)
被 connect 的 Component 会自动在 props 中拥有 dispatch 方法。
笔者似乎还是没有回答dva数据在底层到底是如何存储的,因为找了很多资料还是没有发现,且听后续分解……
Q&A
model底层state存储也是用闭包实现,那么这个东西写出来了,
model是怎么去使用的呢?
参考文档
[学习笔记]JS计数器,闭包和localStorage
React hooks的useState原理