React 및 에이전트 및 이벤트 송신기에 대한 자체 상태 관리
28644 단어 reactwebdevreduxjavascript
간단한 소개
쉽게 주제로 들어가기
나는 적응하는 데 약 6개월이 걸렸다고 생각한다.
'using Redux'
16개월이 되었는데 아직 적응이 안 됐어요'Redux itself'
.개인적으로, 나는 왜 신축성 요구가 있는 대형 응용 프로그램에서 레드ux가 필요한지 확실히 깨달았다. 이 점에서 레드ux는 순수한 하느님이 보낸 것이다.그러나 대부분의 작은 응용 프로그램에 대해 레드ux의 단점은 장점을 초과할 수 있다. 이것은 구체적인 상황에 달려 있다.그러면 상하문 API와 다른 상태 관리 라이브러리는?
다른 모든 가방과 마찬가지로 수중에 있는 항목에 따라 사용자의 요구에 더욱 적합한 대체 방안이 있을 수 있습니다.근데 왜 혼자 해보지 않았어요?그래서 나는 이 문제를 깊이 연구하기 시작했다. 전 세계 국가가 도서관을 관리하는 가장 간단한 방법은 무엇입니까?(네, 스스로 하지 않으려는 이유가 많지만 용서해 주세요)
TLDR- 결과
If you'd rather read the source code ( npm package )그래, 그런데 내가 왜 혼자 해야 하지?
왜 useState와 갈고리가 아닌 에이전트와 이벤트
그래서 제가 이 문제를 해결하기 전에 React를 강제적으로 하는 것을 피하고 싶습니다. 이유는 다음과 같습니다.
대리점
The closest thing that mimics c++ operator overloading in js
첫인상이 될 거예요.그러나 실제로는 다른 편집할 수 없는 함수에 대한 사용자 정의 기능을 정의할 수 있는 패키지입니다.Reflect와 쌍을 이루면 부작용만 발생시키는 정상적인 기능을 유지할 수 있습니다.(이것은 개인적인 관점이므로 논란이 있을 수 있습니다. 있다면 평론에 알려주세요)
const store = {};
const storeProxy = new Proxy(store, {
set: function (obj, prop, value) {
obj[prop] = value;
// my custom set logic
//....
console.log(`I'm setting ${prop} to - `, value);
return true;
},
get: function (target, prop, receiver) {
const obj = Reflect.get(...arguments);
// my custom get logic
//...
return obj;
}
});
현재 storeProxy를 사용하여 저장소를 편집하면 다음과 같습니다storeProxy.foo = "bar";
실행 중인 사용자 정의 설정 논리를 볼 수 있습니다.약간 관찰자가 관찰할 수 있는 사물을 관찰하는 것 같아!방주에서 약 10개의 값을 포함하는 그룹을 만들고 집합 조작수를 계산하는 에이전트를 만들고 값을 팝업해서 이동합니다.값을 변환하는 데는 O (n) 시간이 필요하고, 팝업 값은 O (1) 시간이 필요한 이유를 볼 수 있습니다.
이벤트 송신기
퓨어 리액트를 사용할 때CustomEvents를 사용해 DOM에 할당하는 것도 작용한다.그러나 DOM에서 접근할 수 없는 장면(예를 들어 SSR 또는 SSG 사용Nextjs)에서는 옵션이 아닐 수 있습니다.그 밖에 사건 발사기에서 온 사건은 어느 곳에서도 전파되거나 거품이 생기지 않기 때문에 비교적 작은 자중을 가지고 있다.
훈련하다
나는 최종적으로 코드 라이브러리를 클래스 기반의 방법으로 재구성하지만, 더욱 광범위한 관중을 위해 함수식 방법을 채택할 것이다.
면책 성명에 의하면 나는 이 코드를 시도하지 않았는데 오류가 있을 수 있다.어떤 형식의 건설적인 비판도 환영한다.아래의 코드는 지도가 되어야 하지만, 예상대로 작업할 수도 있다.약속 없음:).TLDR section 중의github 환매 협의가 실행 중입니다.
첫 번째 단계-구축 모듈
// because using document events doesn't work on SSG / SSR
const Emitter = require("events")
const EventEmitter = new Emitter()
// virtually no limit for listeners
EventEmitter.setMaxListeners(Number.MAX_SAFE_INTEGER)
let eventKey = 0
export const createStore = (initObj) => {
// underbar for private methods / vars
const _evName = `default-${eventKey++}`
const _store = cloneDeep(initObj) // preferred deep cloning package, recommend rfdc
const _storeProxy = new Proxy(store, {
set: function (obj, prop, value) {
// apply options, restrictions pertaining to your needs
}
});
// dispatch logic to use when store is updated
const _dispatchEvent = () => {
EventEmitter.emit(_evName)
}
// ... the HOC and update logic
}
이것이 바로 적나라한 판본이다.용서해 주세요.외부에 노출되지 않는 개인 성명을 모의하기 위해 모든 성명 앞에 하단이 있다.
evName의 정의는 여러 저장소에서 이벤트를 구분하기 위한 것입니다.
2단계 - HOC 및 업데이트 로직
// ... the HOC and update logic
const updateStore = obj => {
// only update store when obj has properties
if(Object.getOwnPropertyNames(obj).length < 1) return;
// update logic via storeProxy
Object.getOwnPropertyNames(obj).forEach(key => {
// possible custom logic
_storeProxy[key] = obj[key];
});
// dispatch for EventEmitter
_dispatchEvent();
}
const getStore = () => return {..._store};
const createUseStore = () => {
// purely for rerendering purposes
const [dummy, setDummy] = useState(false);
const rerender = useCallback(() => setDummy(v => !v), [setDummy]);
useEffect(() => {
const eventHandler = () => rerender();
EventEmitter.on(_evName, eventHandler);
return () => EventEmitter.removeListener(_evName, eventHandler);
}, [rerender]);
// only updates when the above event emitter is called
return useMemo(() => {
return [this._store, this.updateStore];
}, [dummy]);
}
return [createUseStore, updateStore, getStore];
}
실제 업데이트 논리와 HOC가 갑자기 도입되면서 1단계가 의미 있게 변하기 시작했다.코드는 간단할 수도 있고 이해할 수도 있지만 논리는 이렇다.2.5단계 - 2단계 MVP 구현
import {createStore} from "where/you/put/your/createStore";
const initMyStore = {
foo: bar
};
const [createUseMyStore, updateMyStore, getMyStore] = createStore(initMyStore);
const useMyStore = createUseMyStore();
export { useMyStore, updateMyStore, getMyStore };
import * as React from "react";
import {useMyStore} from "the/initcode/above";
export default function MyComponent() {
const [store] = useMyStore();
return (
<div>{store?.foo}</div>
)
}
// in another file far far away.....
import {updateStore} from "the/initcode/above";
function aFunctionNestedInside50Functions () {
updateStore({foo: "barbar"});
}
위에서 말한 바와 같이 이것은 기본적인 MVP이다. 이는 글로벌 상태 관리 패키지가 일반적으로 필요로 하는 많은 핵심 기능이 현재 박리되었다는 것을 의미한다. 예를 들어선택적인 상태 업데이트와 이벤트 스케줄링을 허용하기 위해 기능을 확장해 봅시다
3단계 - 기능 확장
//...
// dispatch logic to use when store is updated
// updated keys are emitted to event emitter
const _dispatchEvent = (keys) => {
EventEmitter.emit(_evName, keys)
}
// ... the HOC and update logic
const updateStore = obj => {
// only update store when obj has properties
if(Object.getOwnPropertyNames(obj).length < 1) return;
// keys are stored to pass to dispatchEvent
let keys = [];
// update logic via storeProxy
Object.getOwnPropertyNames(obj).forEach(key => {
// possible custom logic
_storeProxy[key] = obj[key];
keys.push(key);
});
if(keys.length < 1) return;
// dispatch for EventEmitter
_dispatchEvent(keys);
}
const getStore = () => return {..._store};
// watch - which key of the store to watch
const createUseStore = (watch) => {
// purely for rerendering purposes
const [dummy, setDummy] = useState(false);
const rerender = useCallback(() => setDummy(v => !v), [setDummy]);
useEffect(() => {
const eventHandler = keys => {
// Don't rerender if property to watch are not part of the update keys
if(watch && !keys.includes(watch)) return;
rerender();
}
EventEmitter.on(_evName, eventHandler);
return () => EventEmitter.removeListener(_evName, eventHandler);
}, [rerender, watch]);
// only updates when the above event emitter is called
return useMemo(() => {
// return watched property when watch is defined.
if(watch) return [this._store[watch], this,updateStore];
return [this._store, this.updateStore];
}, [dummy, watch]);
}
return [createUseStore, updateStore, getStore];
}
이곳에서 많은 일이 발생했지만 이 모든 것은 기능이'watched'속성 업데이트 시에만 상태 업데이트를 할 수 있도록 하기 위해서입니다.예를 들어, 스토리지가 로 초기화된 경우{
foo: "bar",
fee: "fi",
fo: "fum",
}
하나의 구성 요소export default function myComp () {
const [foo, updateStore] = useMyStore("foo");
return <>{foo}</>
}
이 구성 요소는 에 의해 업데이트되지 않습니다.updateStore({fee: "newFi", fo: "newFum"});
그러나'foo'가 업데이트될 때만 이것은 내가 이 기이한 여정을 시작할 때 실현하고자 하는 주요 기능 중의 하나이다.위에서 언급한github repo는 클래스 기반 방법을 더 많이 제공합니다. 관심이 있으면 보십시오.
결론
나는 네가 어떻게 생각하는지 모르겠지만, 내가 자신의 버전의 개성화된 상태 관리 라이브러리를 만들기 시작했을 때, 나의 전체적인 상태를 위해 새로운 기능을 만드는 것은 그야말로 즐거움이었다. 이것은 내가 레드ux를 만지작거릴 때 거의 겪지 못했던 일이다. 아마도 yak shaving 나의 시간일 것이다.그러나 농담은 그만두고 대다수 용례에서 이렇게 하는 것은 순전히'바퀴 재발명'의 정의이기 때문에 실시와 시도를 스스로 결정하세요. 저는 개인적으로 전 세계 상태에 심각하게 의존하지 않는 재미있는 프로젝트를 추천합니다.
Reference
이 문제에 관하여(React 및 에이전트 및 이벤트 송신기에 대한 자체 상태 관리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/fly/make-your-own-state-management-for-react-with-proxies-1n0m텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)