A simple wrapper around React.createContext that makes it easy to use hooks and context together. It reduces boilerplate and improves type safety, providing a clean and developer-friendly API.
- 🔄 Simple hook-based context creation with minimal boilerplate
- 🛡️ Strict, automatic type inference out of the box
- đź§© Render function support inside the provider for direct context access
- ⚙️ Easy binding of context props with the value hook
- đźš« No need for
useContext(Context)orContext.Consumer— cleaner API
- React ≥ 16.8
- TypeScript (optional, but recommended)
npm i react @recrafter/ctximport React from 'react';
import {createCtx} from '@recrafter/ctx';
// Define a hook with your value:
const useValue = () => React.useState('');
// Context factory takes any hook as the first argument:
const Ctx = createCtx(useValue);
// Now you can use Ctx.use() to get context value:
const Input = () => {
const [value, setValue] = Ctx.use();
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
};
const ResetButton = () => {
const [, setValue] = Ctx.use();
return <button onClick={() => setValue('')}>Reset</button>;
};
// Put Ctx component above in the component tree.
// The Ctx component is the place where the hook is executed.
const App = () => (
<Ctx>
<Input />
<ResetButton />
</Ctx>
);You can also use a render function to get the context value:
const App = () => (
<Ctx>
{([value, setValue]) => (
<>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<button onClick={() => setValue('')}>Reset</button>
</>
)}
</Ctx>
);You can pass properties to the context component:
interface CtxProps {
initialValue: string;
}
const Ctx = createCtx((props: CtxProps) => {
return React.useState(props.initialValue);
});
const App = () => (
<Ctx initialValue="Hello, world!">
<Input />
<ResetButton />
</Ctx>
);Unlike native useContext, this
library prevents using Ctx.use() without declaring the Ctx component higher in
the component tree. If you try to do this:
const Ctx = createCtx(() => React.useState(''), 'inputValue');
const Input = () => {
const [value, setValue] = Ctx.use();
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
};
// MissingCtxProviderError: no provider in the component tree
const App = () => <Input />;You'll get a clear error message:
Context provider is missing. Please mount Ctx(inputValue) component above in the component tree.
The Ctx.inject() - HOC provides an alternative way to consume context values
by injecting them directly into component props.
const useValue = () => {
const [value, setValue] = React.useState('');
return {value, setValue};
};
const Ctx = createCtx(useValue);
type InputProps = {
value: string;
setValue: (value: string) => void;
type: 'password' | 'text';
};
const Input = ({type, value, setValue}: InputProps) => (
<input
type={type}
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
type ResetButtonProps = {setValue: (value: string) => void};
const ResetButton = ({setValue}: ResetButtonProps) => (
<button onClick={() => setValue('')}>Reset</button>
);
// Inject context values directly into component props
const InjectedInput = Ctx.inject(Input);
const InjectedResetButton = Ctx.inject(ResetButton);
const App = () => (
<Ctx>
<InjectedInput type="text" />
<InjectedResetButton />
</Ctx>
);You can also use a mapper function to transform context values. The function can be a regular function or a React hook:
const useValue = () => React.useState('');
const Ctx = createCtx(useValue);
type InputProps = {
value: string;
setValue: (value: string) => void;
type: 'password' | 'text';
};
const Input = ({type, value, setValue}: InputProps) => (
<input
type={type}
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
type ExtraProps = {wrapper: string};
const InjectedInput = Ctx.inject(
([value, setValue], {wrapper}: ExtraProps) => ({
value: `${wrapper}${value}${wrapper}`,
setValue,
}),
Input,
);
const App = () => (
<Ctx>
<InjectedInput type="text" wrapper="--" />
</Ctx>
);Function to create a context.
Parameters:
useValue: Hook that returns the context valuedisplayName?: Optional name for debugging
Returns:
CtxComponent: Context component withuse()andinject()methods
Props:
children: React elements or a render function(value) => ReactNode- Any additional props that are passed to the
useValuehook
Methods:
use(): Value: Hook to get the context valueinject(Component): InjectedComponent: Inject context value directly into component propsinject(useMappedValue, Component): InjectedComponent: Inject transformed context value using a mapper function, that can be a regular function or a React hook
# Install dependencies
npm ci
# Run tests
npm run test
# Build the project
npm run build
# Check code format
npm run formatter:check
npm run formatter:fix
# Analyze code quality
npm run analyzer:code:check
npm run analyzer:code:fix
# Analyze public API quality
npm run analyzer:api:check