React 및 에이전트 및 이벤트 송신기에 대한 자체 상태 관리

간단한 소개


쉽게 주제로 들어가기


나는 적응하는 데 약 6개월이 걸렸다고 생각한다. 'using Redux'16개월이 되었는데 아직 적응이 안 됐어요'Redux itself'.개인적으로, 나는 왜 신축성 요구가 있는 대형 응용 프로그램에서 레드ux가 필요한지 확실히 깨달았다. 이 점에서 레드ux는 순수한 하느님이 보낸 것이다.그러나 대부분의 작은 응용 프로그램에 대해 레드ux의 단점은 장점을 초과할 수 있다. 이것은 구체적인 상황에 달려 있다.
  • 동작이 많이 사용되지 않는다
  • 논리를 분리해야 한다
  • What Dan says

  • 그러면 상하문 API와 다른 상태 관리 라이브러리는?


    다른 모든 가방과 마찬가지로 수중에 있는 항목에 따라 사용자의 요구에 더욱 적합한 대체 방안이 있을 수 있습니다.근데 왜 혼자 해보지 않았어요?그래서 나는 이 문제를 깊이 연구하기 시작했다. 전 세계 국가가 도서관을 관리하는 가장 간단한 방법은 무엇입니까?(네, 스스로 하지 않으려는 이유가 많지만 용서해 주세요)

    TLDR- 결과

    If you'd rather read the source code ( npm package )

    그래, 그런데 내가 왜 혼자 해야 하지?

  • '내가 하나 만들어 봤는데 결과는 다음과 같다'는 말보다 더 좋은 방법이 어디 있겠는가.아마도 가장 좋은 면접 문제의 답안일 것이다.(각종 주 관리 도서관과 당신의 경력을 상세히 묘사한 것이 분명하다)
  • 전 세계 국가 관리가 모호할 수 있는 개념과 메커니즘의 신비한 베일을 벗긴다.
  • 어떻게 시작하는지 알았습니다. 당신의 프로젝트를 위한 맞춤형 제작은 실제적으로 다른 글로벌 상태 관리(예를 들어 Redux)에 들어가는 데 소요되는 시간이 적고 Redux는 상당한 학습 곡선을 가지고 있습니다.
  • 솔직히 말하면 이유가 별로 없어요. 저는 단지 교과서의 형식으로 제 경험을 공유하고 싶어요.레드ux를 배우는 것은 대부분의 사람들과 대규모 응용 프로그램 장면에 이롭다.
  • 왜 useState와 갈고리가 아닌 에이전트와 이벤트


    그래서 제가 이 문제를 해결하기 전에 React를 강제적으로 하는 것을 피하고 싶습니다. 이유는 다음과 같습니다.
  • React 선택 가능(분명)
  • 상점에 대한 보다 세밀한 통제
  • 가장 중요한 것은 저장소를 업데이트할 수 있고 React 구성 요소에서 업데이트 함수를 찾을 필요가 없다는 것이다.
  • 개인적으로, 나는 여러 함수에서 상점 스케줄러를 훈련시키는 것에 싫증이 났다. 왜냐하면 나는 자바스크립트에 더욱 집중하는 인코딩 스타일로 전환하기 시작했기 때문이다.나의 첫 번째 시도는 rxjs의 관찰자와 관찰자를 사용하여 이 점을 실현하는 것이다.이것은 가능하지만, rxjs의 의존성은 최소한의 묶음 크기가 필요한 사이트에 있어서 무겁다.따라서 한동안의 연구를 통해 대리와 사건의 짝짓기 감각은 완벽한 선택이었다.

    대리점

    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단계가 의미 있게 변하기 시작했다.코드는 간단할 수도 있고 이해할 수도 있지만 논리는 이렇다.
  • 이벤트 송신기 정의(전역)
  • js 대상 형식의 저장소를 만들었다
  • 사용자 정의 논리 프록시 저장소를 위한 프록시를 만듭니다.
  • 업데이트 스토어를 정의하고 프록시에 키의 값을 설정한 다음 이벤트를 분배
  • 현재 스토리지로 돌아가는 getStore를 정의합니다.
  • 저장 및 업데이트 함수를 반환하는 HOC를 정의합니다.
  • 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이다. 이는 글로벌 상태 관리 패키지가 일반적으로 필요로 하는 많은 핵심 기능이 현재 박리되었다는 것을 의미한다. 예를 들어
  • 선택적 이벤트 스케줄링
  • 선택적 속성 감시
  • 불변성 또는 선택적 불변성
  • Container predictability
  • 기본적으로 다른 전 세계 주 관리 패키지는 많은 보장 조치를 제공했다.
  • 대부분의 간단한 프로그램에 대해 위의 코드 + 'get' 에서 깊이 있는 복사/깊이 동결된 버전을 되돌려주면 충분합니다.
    선택적인 상태 업데이트와 이벤트 스케줄링을 허용하기 위해 기능을 확장해 봅시다

    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 나의 시간일 것이다.그러나 농담은 그만두고 대다수 용례에서 이렇게 하는 것은 순전히'바퀴 재발명'의 정의이기 때문에 실시와 시도를 스스로 결정하세요. 저는 개인적으로 전 세계 상태에 심각하게 의존하지 않는 재미있는 프로젝트를 추천합니다.

    좋은 웹페이지 즐겨찾기