atom管理state,
selector选择atom后做拦截中间层处理
import { useState, useCallback, useEffect } from 'react';
/**
* 1.订阅
* 2.发布
* 3.更新
* 4.快照
* 5.事件发布
*/
interface Dissconnect {
disconnect: () => void
}
class StateFul<T>{
constructor(protected value: T) {
}
private listener = new Set<(value: T) => void>();
public subscribe(callback: (value: T) => void): Dissconnect {
this.listener.add(callback);
return {
disconnect: () => {
this.listener.delete(callback)
}
}
}
public list() {
return this.listener;
}
public snapShot() {
return this.value;
}
public update(value: T) {
if (this.value !== value) {
this.value = value;
this.emit();
}
}
protected emit() {
this.listener.forEach(callback => callback(this.value));
}
}
class Atom<T> extends StateFul<T>{
public setState(value: T): void {
this.update(value);
}
}
class Selector<T> extends StateFul<T>{
constructor(private readonly generate: SelectorGenerator<T>) {
super(undefined as any);
this.value = this.generate({get:(dep:Atom<any>)=>this.addDep(dep)});
}
private register = new Set<Atom<any>>();
public addDep(dep:Atom<any>){
if(!this.register.has(dep)){
dep.subscribe(()=>this.updateSelector())
this.register.add(dep)
}
return dep.snapShot();
}
private updateSelector(){
this.update(this.generate({get:(dep:Atom<any>)=>this.addDep(dep)}));
}
}
export function atom<V>(value: { key: string, defaultValue: V }) {
return new Atom(value.defaultValue);
}
type SelectorGenerator<V> = (context: { get: <T>(dep: Atom<T>) => T }) => V;
export function selector<V>(value: { key: string; get: SelectorGenerator<V> }) {
return new Selector(value.get);
}
export function useRecoilState<T>(atom: StateFul<T>) {
const value = useRecoilValue(atom);
return [value,
useCallback((value: T) => {
atom.update(value);
}, [atom])
] as const;
}
export function useRecoilValue<T>(value: StateFul<T>) {
const [state, setState] = useState({});
console.log(123, value.list(), state)
// value没变就不执行注册
useEffect(() => {
// value触发时,将setState注册到value的callback列表中,
// update被调用时,触发setState,setState触发组件重新渲染。
const { disconnect } = value.subscribe(() => setState({}));
return () => {
disconnect();
}
}, [value])
return value.snapShot();
}
import React, { ChangeEvent } from 'react';
import logo from './logo.svg';
import './App.css';
import { atom, selector, useRecoilState, useRecoilValue } from './recoil/index';
const textState = atom({
key: 'textState',
defaultValue: 'Hello World'
});
const charCountState = selector({
key: 'charCountState',
get: ({ get }) => {
const text = get(textState);
return text ? text.length : 0;
},
});
function App() {
// const txt = useRecoilValue(textState);
const [text, setText] = useRecoilState(textState);
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
setText(event.target.value);
}
const a = useRecoilValue(charCountState)
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
{/* Edit <code>src/App.tsx</code> and save to reload. */}
<input type='text' value={text} onChange={onChange}></input>
</p>
<a
className="App-link"
href="<https://reactjs.org>"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
{text}
<p></p>
selector:
{a}
{/* {txt} */}
</header>
</div>
);
}
export default App;