簡體   English   中英

如何在組件中使用 useHook,而不是將其返回的值作為 prop 傳遞給使用 react 和 typescript 的組件?

[英]How to use the useHook in the component rather than passing the value returned from it as a prop to the components using react and typescript?

我想在組件本身中使用 useHook,而不是使用 react 和 typescript 將其作為道具傳遞給每個組件。

我想做什么? 我有一個名為 useRefresh 的 useHook,它返回 isLoading state。 This isLoading state 用於在每個頁面中顯示加載指示器。

所以我有三個頁面,只要這個 isLoading 為真,就應該在這些頁面中顯示一個加載指示器。

下面是我的代碼,

function App(){
    const user = useGetUser(); 
    return (
        <Router>
            <Switch>
                <Route
                    path="/"
                    render={props: any => (
                        user ? (<Main {...props} />) : (
                            <LoginPage/>
                        );
                    )}
                </Route>
            </Switch>
        </Router>
    );
}

export function useLoad() {
    const { refetch: refetchItems } = useGetItems();
    const { refetch: refetchOwnedItems } = useListOwnedItems();
    return async function() {
        await refreshCompany();
        refetchItems();
        refetchOwnedItems();
    };  
}

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);
    return (
        <Router>
            <Switch>
                <Route 
                    path="/" 
                    render={routeProps => (
                        <FirstComp {...routeProps} />
                    )}
                />
                <Route 
                    path="/items" 
                    render={routeProps => (
                        <SecondComp {...routeProps} />
                    )}
                />
                //many other routes like these
            </Switch>
        </Router>
    );
}


function FirstComp () {
    return(
        <Wrapper>
            //some jsx
        </Wrapper>
    );
}

function SecondComp () {
    return(
        <Wrapper>
            //some jsx
        </Wrapper>
    );
}

現在我想將 isLoading state 傳遞給主要組件中的每個組件....所以我已經像下面一樣傳遞了它,

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


function FirstComp ({isLoading}: Props) {
    return(
        <Wrapper>
             displayIndicatorWhen(isLoading);
            //some jsx
        </Wrapper>
    );
}

function SecondComp ({isLoading}: Props) {
    return(
        <Wrapper>
            displayIndicatorWhen(isLoading);
            //some jsx
        </Wrapper>
    );
}

這行得通。 但對我來說似乎不是一個正確的方法.. 我不想通過這個 isLoading state 作為這些組件中的每一個的道具。 其中有10多個。

有沒有其他方法可以做到這一點。 有人可以幫我解決這個問題。 謝謝。

最常見的解決方案是創建一個包含整個組件樹的上下文。 此上下文包含您的鈎子拉入的 state

////LoadingContext.tsx
const LoadingContext =  createContext();

const LoadingContextProvider = () => {
    const [isLoading, setIsLoading] = useState(false);

    return (
        <LoadingContextProvider.Provider
            value={{
               isLoading,
               setIsLoading
            }}
        />
    )

}

export const useLoading = () => useContext(LoadingContext);

您需要將上下文圍繞將調用useLoading的任何內容:

import { LoadingContextProvider } from './LoadingContext' //or wherever this is relative to Main.tsx 

<LoadingContextProvider>
    <Router>
       ...(router stuff)
    </Router>
</LoadingContextProvider>

現在您可以在較低級別的組件中調用 useLoading。

//in another file defining a lower-level component:
import { useLoading } from '../../LoadingContext' //or wherever the context stuff is relative to this component definition


const FirstComp = () => 
    const [isLoading, setIsLoading] = useLoading();

    const handleClick = () => {
        setIsLoading(true);
        callMyApi().then(() => setIsLoading(false));
    } 

    if(isLoading){
        return <LoadingGif />
    }
    else{
        return <div onClick={handleClick}>Click me!</div>
    }
)}

你想在這里完成的是全局 state。 有很多方法可以做到,但我認為最簡單的是原生的 React Context API。

您所要做的就是創建一個ContextProvider ,然后在組件中使用useContext掛鈎來訪問它提供的值。

這是一個適用於您的案例的示例:

主.js

export const LoadingContext = React.createContext(true); //creating and exporting the context

function Main ({user}: Props) {
    const isLoading = useAnother(user.id); //fetching isLoading here from useHook
    return (
        <LoadingContext.Provider value={isLoading}> {/* providing the value to the children */}
            <Router>
                <Switch>
                    <Route 
                        path="/" 
                        render={routeProps => (
                            <FirstComp {...routeProps} />
                        )}
                    />
                    <Route 
                        path="/items" 
                        render={routeProps => (
                            <SecondComp {...routeProps} />
                        )}
                    />
                    //many other routes like these
                </Switch>
            </Router>
        </LoadingContext.Provider>
    );
}

export default Main;

其他組件

import {LoadingContext} from './Main.js'

function FirstComp ({}: Props) {
    const isLoading = useContext(LoadingContext); //accessing the value
    return(
        <Wrapper>
             displayIndicatorWhen(isLoading);
            //some jsx
        </Wrapper>
    );
}

function SecondComp ({}: Props) {
    const isLoading = useContext(LoadingContext); //accessing the value
    return(
        <Wrapper>
            displayIndicatorWhen(isLoading);
            //some jsx
        </Wrapper>
    );
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM