Open alian926 opened 3 years ago
// vanilla
// 通过闭包管理状态
export default function create(
createState
) {
// 状态
let state
// 订阅函数集合
const listeners = new Set()
/**
* 设置状态
* @param paratial 部分的数据, 会同原始数据一起整合到新对象中, 这个单词用的好呀
* @param replace 使用新数据完全替换原始数据
* */
const setState = (partial, replace) => {
const nextState =
typeof partial === 'function' ?
partial(state) :
partial
// 浅比较
if (nextState !== state) {
const previousState = state
state = replace ?
nextState :
Object.assign({}, state, nextState)
// 通知订阅函数更新, 传入新值和旧值
listeners.forEach((listener) => listener(state, previousState))
}
}
/**
* 获取状态
* @returns state
*/
const getState = () => state
/**
* 内部 通过 Selector 设置订阅函数
* @param listener
* @param selector 获取状态,默认为getState,可由用户自定义传入
* @param equalityFn 浅比较, 默认是Object.is
* @returns 取消订阅函数
*/
const subscribeWithSelector = (
listener,
selector = getState,
equalityFn = Object.is
) => {
// 当前状态切片, 闭包存值, 由于允许用户自定义传入,命名的好呀
let currentSlice = selector(state)
function listenerToAdd() {
const nextSlice = selector(state)
if (!equalityFn(currentSlice, nextSlice)) {
// 更新状态数据
const previousSlice = currentSlice
listener((currentSlice = nextSlice), previousSlice)
}
}
listeners.add(listenerToAdd)
// Unsubscribe
// 返回取消订阅函数
return () => listeners.delete(listenerToAdd)
}
/**
* 对外暴露的订阅函数
* @param listener
* @param selector
* @param equalityFn
* @returns
*/
const subscribe = (
listener,
selector,
equalityFn
) => {
// 如果设置了 selector 或 equalityFn 调用 subscribeWithSelector, 否则直接订阅,节省闭包消耗的内存
if (selector || equalityFn) {
return subscribeWithSelector(
listener,
selector,
equalityFn
)
}
listeners.add(listener)
// Unsubscribe
return () => listeners.delete(listener)
}
/**
* 清除所有订阅
* @returns
*/
const destroy = () => listeners.clear()
/**
* 对外暴露的函数集合
*/
const api = {
setState,
getState,
subscribe,
destroy
}
state = createState(setState, getState, api)
return api
}
// index.js
import {
useEffect,
useLayoutEffect,
useReducer,
useRef
} from 'react'
import createImpl from './vanilla'
export * from './vanilla'
const isSSR =
typeof window === 'undefined' ||
!window.navigator ||
/ServerSideRendering|^Deno\//.test(window.navigator.userAgent)
const useIsomorphicLayoutEffect = isSSR ? useEffect : useLayoutEffect
/**
* 经过react包装过的
* @param createState
* @returns
*/
export default function create(
createState
) {
// createImpl 是 原生 create别名, api是原生状态集合
const api =
typeof createState === 'function' ? createImpl(createState) : createState
const useStore = (
selector = api.getState,
equalityFn = Object.is
) => {
const [, forceUpdate] = useReducer((c) => c + 1, 0)
const state = api.getState()
const stateRef = useRef(state)
const selectorRef = useRef(selector)
const equalityFnRef = useRef(equalityFn)
const erroredRef = useRef(false)
const currentSliceRef = useRef()
if (currentSliceRef.current === undefined) {
currentSliceRef.current = selector(state)
}
let newStateSlice
let hasNewStateSlice = false
// The selector or equalityFn need to be called during the render phase if
// they change. We also want legitimate errors to be visible so we re-run
// them if they errored in the subscriber.
if (
stateRef.current !== state ||
selectorRef.current !== selector ||
equalityFnRef.current !== equalityFn ||
erroredRef.current
) {
// Using local variables to avoid mutations in the render phase.
newStateSlice = selector(state)
hasNewStateSlice = !equalityFn(
currentSliceRef.current,
newStateSlice
)
}
// Syncing changes in useEffect.
useIsomorphicLayoutEffect(() => {
if (hasNewStateSlice) {
currentSliceRef.current = newStateSlice
}
stateRef.current = state
selectorRef.current = selector
equalityFnRef.current = equalityFn
erroredRef.current = false
})
const stateBeforeSubscriptionRef = useRef(state)
useIsomorphicLayoutEffect(() => {
const listener = () => {
try {
const nextState = api.getState()
const nextStateSlice = selectorRef.current(nextState)
if (
!equalityFnRef.current(
currentSliceRef.current,
nextStateSlice
)
) {
stateRef.current = nextState
currentSliceRef.current = nextStateSlice
forceUpdate()
}
} catch (error) {
erroredRef.current = true
forceUpdate()
}
}
const unsubscribe = api.subscribe(listener)
if (api.getState() !== stateBeforeSubscriptionRef.current) {
// 订阅之前便发生数据的变更了,需要通知订阅更新
listener() // state has changed before subscription
}
return unsubscribe
}, [])
return hasNewStateSlice ?
newStateSlice :
currentSliceRef.current
}
// 向函数中混进方法
Object.assign(useStore, api)
useStore[Symbol.iterator] = function () {
console.warn(
'[useStore, api] = create() is deprecated and will be removed in v4'
)
const items = [useStore, api]
return {
next() {
const done = items.length <= 0
return {
value: items.shift(),
done
}
},
}
}
return useStore
}
参考 zustand 状态管理库
月色
这句话的潜意识是:
来自一篇经典社会学实验的36个问题
第一组问题
假如可以选择世界上任何人,你希望邀请谁共进晚餐?
你希望成名吗?在哪一方面?
拨打电话前,你会先练习要说的话吗?为什么?
对你来说,怎样才算是“完美”的一天?
上一次唱歌给自己听是什么时候?唱歌给别人听又是什么时候呢?
假如你能够活到90岁,并且你可以选择让你的心智或身体在后60年一直停留在30岁,你会选择哪一个?
关于未来你可能怎么死,你有自己的秘密预感吗?
列举3个你和对方共同拥有的特质。
你的人生中最感恩的事情是什么?
假如可以改变你成长过程中的任何事,你希望有哪些改变?
用4分钟的时间,尽可能详细地向对方讲述你的人生故事。
假如明天早上起床后能获得任何一种能力或特质,你希望是什么?
第二组问题
假如有颗水晶球能告诉你关于自己、人生或未来的一切真相,你想知道什么?
有什么事想做很久了?还没去做的原因是?
你人生最大的成就是什么?
友情中你最重视哪一个部份?
你最珍贵的回忆是什么?
你最糟糕的回忆是什么?
如果你知道自己将在一年内突然死去,你会改变自己目前的生活方式吗?为什么?
友情对你而言意味着什么?
爱和感情在你生命里扮演什么样的角色?
轮流分享你认为对方拥有的比较好的性格特点。各自提5点。
你的家庭关系亲密温暖吗?你是否觉得自己的童年比大部分人快乐?
你与母亲的关系如何?
第三组问题
说出3个含有“我们”并且符合实际情况的句子,比如“我们现在都在这个房间里”。
完成这个句子:“我希望可以跟某个人分享——”。
如果你要成为对方的密友,有什么事是他或她需要知道的?
告诉对方你喜欢他或她的什么地方(回答此题必须非常诚实,要说出你可能不会对刚认识的人说的事)。
和对方分享你人生中尴尬的时刻。
上次在别人面前哭是什么时候?自己哭又是什么时候?
告诉对方,你现在喜欢他或她什么地方。
有什么事是绝对不能开玩笑的?
如果你今天晚上就会死掉,而且无法与任何人联系,你最遗憾还没有告诉别人什么事?为什么还没说呢?
你的房子起火了,你所有的东西都在里面。在救出所爱的人和宠物后,你还有时间可以安全地抢救出最后一件东西。你会拿什么?为什么?
在你所有家人当中,谁的死对你的打击会最大?为什么?
分享你人生中的一个问题,问对方遇到这样的问题会怎么做。同时也请对方告诉你,在他或她看来,你对这个问题的感受是什么。
其实这个实验说明了一个道理:人们之间的关系从疏离到亲密,需要双方不断深入剖析自己的内心并互相坦白。 实验后续还有一个有趣的发现,人们之间的亲密程度,跟他们对同样问题的回答是否相似并无直接关系,也就是说,“三观相同”并不是两个人关系亲密的必要条件。
所以这个实验的最终结论是:两个陌生人要迅速建立亲密关系,需要依靠双方挖掘并坦诚内心的真实想法。