【问题标题】:How to use a hook instead of context provider using react and typescript?如何使用钩子而不是使用反应和打字稿的上下文提供者?
【发布时间】:2020-06-10 07:22:56
【问题描述】:

我想在主组件的每个组件中访问 isLoading 状态。基本上,当 useAnother 钩子中的 load() 开始和结束时,我将加载状态设置为 true 和 false。

下面是我没有上下文提供者的代码,

function useAnother(Id: string) {
    const [compId, setCompId] = React.useState(undefined);
    const [isLoading, setIsLoading] = React.useState(false);
    const comp = useCurrentComp(Id);
    const load = useLoad();
    if (comp && comp.id !== compId) {
        setCompId(comp.id);
        const prevCompId = compId !== undefined;
        if (prevCompId) {
            setIsLoading(true);
            load().then(() => {
                setIsLoading(false);
            });
        }
    }
}

function Main ({user}: Props) {
    useAnother(user.id); //fetching isLoading here from useHook
    return (
        <Wrapper>
            <React.suspense>
                <Switch>
                    <Route 
                        path="/" 
                        render={routeProps => (
                            <FirstComp {...routeProps} />
                        )}
                    />
                    <Route 
                        path="/items" 
                        render={routeProps => (
                            <SecondComp {...routeProps} />
                        )}
                    />
                   //many other routes like these
                </Switch>
            </React.suspense>
        </Wrapper>
    );
}

现在使用上下文提供程序

interface LoadingContextState {
    isLoading: boolean;
    setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
}

const initialLoadingState: LoadingContextState = {
    isLoading: false, setIsLoading: () => {},
};

export const LoadingContext = React.createContext<LoadingContextState>(
    initialLoadingState
);

export const LoadingContextProvider: React.FC = ({ children }) => {
    const [isLoading, setIsLoading] = React.useState<boolean>(false);

    return (
         <LoadingContext.Provider
             value={{
                 isLoading,
                 setIsLoading,
             }}
         >
             {children}
         </LoadingContext.Provider>
     );
 };

 function App() {
     return (
         <LoadingContextProvider>
             <Main/>
         </LoadingContextProvider>
     );
 }

 function useAnother(Id: string) {
    const [compId, setCompId] = React.useState(undefined);
    const {setIsLoading} = React.useContext(LoadingContext);
    const comp = useCurrentComp(Id);
    const load = useLoad();
    if (comp && comp.id !== compId) {
        setCompId(comp.id);
        const prevCompId = compId !== undefined;
        if (prevCompId) {
            setIsLoading(true);
            load().then(() => {
                setIsLoading(false);
            });
        }
    }
}

function Main ({user}: Props) {
    useAnother(user.id);
    return (
        <Wrapper>
            <React.suspense>
                <Switch>
                    <Route 
                        path="/" 
                        render={routeProps => (
                            <FirstComp {...routeProps} />
                        )}
                    />
                    <Route 
                        path="/items" 
                        render={routeProps => (
                            <SecondComp {...routeProps} />
                        )}
                    />
                   //many other routes like these
                </Switch>
            </React.suspense>
        </Wrapper>
    );
}

function FirstComponent () {
    const {isLoading} = React.useContext(LoadingContext);
    return (
        <Wrapper isLoading={isLoading}/>
    );
}

这行得通。但我不想使用上下文提供程序,是否可以为此使用钩子而不是上下文。

有人可以帮我解决这个问题吗?谢谢。

}

现在使用上下文提供程序

interface LoadingContextState {
    isLoading: boolean;
    setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
}

const initialLoadingState: LoadingContextState = {
    isLoading: false, setIsLoading: () => {},
};

export const LoadingContext = React.createContext<LoadingContextState>(
    initialLoadingState
);

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    如果您选择从 useAnother 挂钩返回 isLoading 的值,这是可能的。

    function useAnother(Id: string) {
        const [compId, setCompId] = React.useState(undefined);
        const [isLoading, setIsLoading] = React.useState(false);
        const comp = useCurrentComp(Id);
        const load = useLoad();
        if (comp && comp.id !== compId) {
            setCompId(comp.id);
            const prevCompId = compId !== undefined;
            if (prevCompId) {
                setIsLoading(true);
                load().then(() => {
                    setIsLoading(false);
                });
            }
        }
    
        return isLoading; // return the isLoading value from the hook
    }
    

    当您在Main 组件中调用useAnother 挂钩时,您可以获得isLoading 值并将其作为道具传递给Main 组件的子组件。 例如,

    const isLoading = useAnother(user.id)
    
    // when you render FirstComp pass isLoading as prop also, the FirstComp
    // needs to have appropriate code for handling the `isLoading` value
     <FirstComp {...routeProps} isLoading={isLoading} />
    

    这种方法的问题,假设您有另一个组件是 FirstComponent 的子组件,它也需要 isLoading 值。要提供 isLoading 值,您必须通过 isLoading 在层次结构中的多个组件中钻取道具,这是一种反模式。

    这就是为什么我建议继续使用上下文 API 方法,

    在使用 typescript 时,使用上下文可能会生成一些复杂的样板代码,但它可以避免您在组件层次结构中钻取 prop。

    【讨论】:

    • 嗯,谢谢..但是不可能在一些新的使用钩子中返回 state 和 setstate 并在 useAnother 钩子中使用它来设置状态并在第一或第二个新的使用钩子中访问 isLoading 状态组件?
    • 在这种情况下,新钩子将在第一个和第二个组件中创建一组新的局部状态变量。不会有共享状态。
    • hmm 所以在我的例子中不能使用新的钩子。
    • 理论上是可能的,但它需要比简单的上下文更复杂的样板代码才能工作。
    猜你喜欢
    • 2022-01-27
    • 2019-04-19
    • 2021-06-30
    • 2023-04-07
    • 1970-01-01
    • 2020-12-19
    • 2023-03-29
    • 2021-08-24
    • 2021-07-09
    相关资源
    最近更新 更多