📰 IT Security NachrichtenRevolut breach exposes widespread security weakness -- trust(17.09.2026 um 21:00 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.156.0-alpha.1 (18.09.2026)(18.09.2026 um 01:24 Uhr)
🐧 Linux TippsDSA-6506-1 chromium - security update(17.09.2026 um 02:00 Uhr)
🎥 VideosShannon Morse: The COOLEST Tech I Saw at IFA 2026!(18.09.2026 um 01:30 Uhr)
🕵️ SicherheitslückenCVE-2023-4751 | vim up to 9.0.1247 heap-based overflow(18.09.2026 um 00:34 Uhr)
📰 IT Security NachrichtenRevolut breach exposes widespread security weakness -- trust(17.09.2026 um 21:00 Uhr)
🔧 AI Nachrichten GitHub Release: openai/codex vrust-v0.156.0-alpha.1 (18.09.2026)(18.09.2026 um 01:24 Uhr)
🐧 Linux TippsDSA-6506-1 chromium - security update(17.09.2026 um 02:00 Uhr)
🎥 VideosShannon Morse: The COOLEST Tech I Saw at IFA 2026!(18.09.2026 um 01:30 Uhr)
🕵️ SicherheitslückenCVE-2023-4751 | vim up to 9.0.1247 heap-based overflow(18.09.2026 um 00:34 Uhr)
🔧 Programmierung 🕛 vor 1 Jahr 9 Min Lesezeit
0

Swapable React context without breaking Rules of Hooks and your neck

↗ Quelle (dev.to)
🗣️ Stimme:

I bet that somewhere in your React project codebase, you’ve encountered this type of code:




CODE
import { createContext, useContext, useState } from "react";

interface ThemeContext {
theme: string;
setTheme: (theme: string) => void;
}

const ThemeContext = createContext<ThemeContext | null>(null);

interface ThemeContextProviderProps {
children: React.ReactNode;
}

export const ThemeContextProvider = ({ children }: ThemeContextProviderProps) => {
const [theme, setTheme] = useState("light");

return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};

export const useTheme = () => {
const ctx = useContext(ThemeContext);

if (!ctx) throw new Error("No ctx found");

return ctx;
};






When using TypeScript, we often assert the context value in the useTheme hook to avoid TypeScript warnings about potential null values. However, this pattern hides the ThemeContext instance itself, which can be crucial in certain scenarios, as we’ll see in this article.






Problem



In one of my projects, I had a table component with sortable columns. Each sorting component needed access to search parameters which was handled by React context up in the tree.



This table component was used across multiple views, some of them had a special ParamsContext implementation. Example:




CODE
import { createContext, ReactNode, useContext, useState } from "react";

interface MostUsedContextType {
params: {
query: string;
page: number;
sortBy?: string;
sortOrder?: string;
};
setParams: (params: MostUsedContextType["params"]) => void;
}

const MostUsedContext = createContext<MostUsedContextType | null>(null);

const MostUsedContextProvider = ({ children }: { children: ReactNode }) => {
const [params, setParams] = useState<MostUsedContextType["params"]>({
// asume that it is from the URL
query: "",
page: 1,
sortBy: undefined,
sortOrder: undefined,
});

return (
<MostUsedContext.Provider value={{ params, setParams }}>
{children}
</MostUsedContext.Provider>
);
};

function useMostUsedContext() {
const ctx = useContext(MostUsedContext);

if (!ctx) throw new Error("No ctx found");

return ctx;
}

// special context

interface SpecialContextType {
params: {
query: string;
page: number;
sortBy?: string;
range: "all" | "week" | "month" | "year";
date?: string;
sortOrder?: string;
};
setParams: (params: SpecialContextType["params"]) => void;
}

const SpecialContext = createContext<SpecialContextType | null>(null);

const SpecialContextProvider = ({ children }: { children: ReactNode }) => {
const [params, _setParams] = useState<SpecialContextType["params"]>({
// asume that it is from the URL
query: "",
range: "all",
date: undefined,
page: 1,
sortBy: undefined,
sortOrder: undefined,
});

const setParams = (params: SpecialContextType["params"]) => {
const newParams = {
...params,
};

// some aditional logic

_setParams(newParams);
};

// more special logic

return (
<SpecialContext.Provider value={{ params, setParams }}>
{children}
</SpecialContext.Provider>
);
};

function useSpecialContext() {
const ctx = useContext(SpecialContext);

if (!ctx) throw new Error("No ctx found");

return ctx;
}






In example below you can see that one context is just handling params and the other one is doing more business logic. Using useMostUsedContext in mentioned sorting component will limit it's flexibility. So I had to find more flexible approach:






What are the options?




  1. Higher-Order Component (HOC)

  2. Using the useParams hook in the parent component

  3. Render props pattern

  4. Passing a hook as props (?)

  5. Passing the context instance as prop



Let’s discuss each approach with examples. First, we define a base ParamsContext and Sorting component so we can work on something together:




CODE
interface ParamsContextType {
params: {
sortBy?: string;
sortOrder?: string;
page?: number;
pageSize?: number;
search?: string;
};
setParams: (params: ParamsContextType["params"]) => void;
}

const ParamsContext = createContext<ParamsContextType | null>(null);

interface ParamsContextProviderProps {
children: ReactNode;
}

export const ParamsContextProvider = ({
children,
}: ParamsContextProviderProps) => {
const [params, setParams] = useState<ParamsContextType["params"]>({
sortBy: undefined,
sortOrder: undefined,
});

return (
<ParamsContext.Provider value={{ params, setParams }}>
{children}
</ParamsContext.Provider>
);
};

export const useParams = () => {
const ctx = useContext(ParamsContext);

if (!ctx) throw new Error("No ctx found");

return ctx;
};

// Sorting.tsx

function Sorting({
params,
setParams,
sortKey,
}: {
params: {
sortBy?: string;
sortOrder?: string;
};
setParams: (params: { sortBy?: string; sortOrder?: string }) => void;
sortKey: string;
}) {
return <div>Sorting</div>;
}









Higher-Order Component (HOC)


A classic way to handle this scenario is to create a wrapWithContext function. For each variation of the ParamsProvider, you create a separate component wrapped with the specific context:




CODE
function wrapWithContext<
T extends Record<PropertyKey, any>,
P extends Record<PropertyKey, any>
>(InjectableContext: Context<T | null>, Component: ComponentType<P>) {
return (props: Omit<ComponentPropsWithoutRef<typeof Component>, keyof T>) => {
const ctx = useContext(InjectableContext); // use(InjectableContext)

if (!ctx) throw new Error("No ctx found");

const p = {
...props,
...ctx,
} as unknown as P;

return <Component {...p} />;
};
}

const SortingWithParams = wrapWithContext(ParamsContext, Sorting);

export default function View() {
return (
<ParamsContextProvider>
<SortingWithParams sortKey="firstName" />
</ParamsContextProvider>
);
}






This approach is flexible — you can pass any context instance to the HOC. However, it can lead to excessive component variables as your project grows, creating unnecessary duplication.






Using the useParams Hook in the Parent


This one is simple. In parent component where you render Sorting component, just use useParams hook and pass all required props to Sorting component. There are cases where it is not possible, for example in render functions in which you cannot invoke hooks.




CODE
function View2() {
const ctx = useParams();

return (
<Sorting
params={ctx.params}
setParams={ctx.setParams}
sortKey="firstName"
/>
);
}

function Providers() {
return (
<ParamsContextProvider>
<View2 />
</ParamsContextProvider>
);
}

// -------------------------------------

import { createColumnHelper } from "@tanstack/react-table";

const helper = createColumnHelper<SomeType>();

const defaultColumns = [
helper.display({
id: "firstName",
cell: (props) => {
return <Text>{props.row.original.firstName}</Text>;
},
header: () => {
const ctx = useParams(); // ❌ this is not a component -> invalid hook call!

return (
<Sorting
params={ctx.params}
setParams={ctx.setParams}
sortKey="firstName"
>
First Name
</Sorting>
);
},
}),
];






In the end it's not that flexible, you cannot use useParams hook in the same component where you render provider and in the second example with table you have to create wrapper component to fix that hook call (or use other approaches from this article).






Render Props Pattern


The render props pattern introduces a flexible and reusable way to inject context:




CODE
const InjectParamsContext = ({
children,
}: {
children: (ctx: ParamsContextType) => ReactNode;
}) => {
const ctx = useParams();

return children(ctx);
};

// or

const InjectContext = <T,>({
Context,
children,
}: {
Context: Context<T>;
children: (ctx: NonNullable<T>) => ReactNode;
}) => {
const ctx = useContext(Context);

if (!ctx) throw new Error("No ctx found");

return children(ctx);
};

function View3() {
return (
<InjectContext Context={ParamsContext}>
{(ctx) => (
<Sorting
params={ctx.params}
setParams={ctx.setParams}
sortKey="firstName"
/>
)}
</InjectContext>
);
}






As you can see we create a dedicated or generic "injector". What's important in this example is that you might find it similar to using just a hook, but there is one big difference, components with render props are swappable, you could just make a ternary oprator in View3 component and pass different context instance to injector or use different dedicated injector. For new React devs it might look a bit too verbose at first but many libraries use render props pattern with success (






Passing context instance as props



Now the last option and one that I finally used with that project I mentioned above. You prepare a special useContext hook that will check for Provider existance based on provided context instance in parameter to get rid of null value and the consumer component will use it, not a wrapper or parent!:




CODE
// special useContext wrapper hook
function useSafeContext<T>(Context: Context<T>) {
const ctx = useContext(Context);

if (!ctx) throw new Error("No ctx found");

return ctx;
}

// we modify the Sorting component to use the new
// hook or we can just create a onetime wrapper
// for it
function SortingImproved({
sortKey,
Context,
}: {
sortKey: string;
Context: Context<{
params: { sortBy?: string; sortOrder?: string };
setParams: (params: { sortBy?: string; sortOrder?: string }) => void;
} | null>;
}) {
const ctx = useSafeContext(Context);

return <div>Sorting</div>;
}

// just pass the context that meet component type constraints
function View4() {
return (
<ParamsContextProvider>
<SortingImproved sortKey="firstName" Context={ParamsContext} />
</ParamsContextProvider>
);
}






In my opinion, it is a very powerful pattern, though mainly suited for niche use cases. Nevertheless, it is worth knowing. It is also React Compiler-safe, and, what's more, you can utilize the new use function from React 19. This function can replace the useContext hook in all cases and has a significant advantage: it can be rendered conditionally. In the future, it may even be used within the useMemo hook to prevent unnecessary re-renders, which often occur with React contexts.






Conclusion



React context offers a lot of flexibility, especially when combined with the patterns discussed. However, be cautious of performance bottlenecks caused by frequent re-renders. For complex state management needs, consider alternatives like .



Everything here was tested on React@19



Thanks for reading! I hope you found this article helpful.



Feel free to connect with me on .

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
2 Quellen
The amount of e-waste caused by AI is underestimated: we can’t only include the servers
1 Quelle
Crusoe raises $3.9B to build massive data centers and small modular “AI factories”
1 Quelle
GitHub Release: openai/codex vrust-v0.156.0-alpha.1 (18.09.2026)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Swapable React context without breaking Rules of Hooks and your neck

Thematisch verwandte Begriffe: Swapable, React, context, without · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...