react-flexible-contexts
TypeScript icon, indicating that this package has built-in type declarations

0.0.15 • Public • Published

React flexible contexts

The goal of this project is to increase flexibility of native context API in react.

Dynamic Context

TL; DR: allows optimizing consuming context without much effort.

Let's consider examples when we want to have context with value of type

type Value = {
    firstname: string;
    lastname: string;
    setFirstname: (firstname: string) => void;
    setLastname: (lastname: string) => void;
}

Creating Dynamic context:

import { DynamicContext } from "react-flexible-contexts";

const UserContext = DynamicContext.create<Value>(); // you can pass default value too but it is not required

const ParentComponent = () => {
	const [firstname, setFirstname] = useState("Nick");
	const [lastname, setLastname] = useState("Fury");

	return (
		<UserContext.Provider
			value={{ firstname, lastname, setFirstname, setLastname }}
		>
			<ChildComponent />
		</UserContext.Provider>
	);
};

Or you can create destructured context

import { DynamicContext } from "react-flexible-contexts";

const UserDestructuredContext = DynamicContext.createDestructured<Value>(); // you can pass default value too but it is not required

const ParentComponent = () => {
	const [firstname, setFirstname] = useState("Nick");
	const [lastname, setLastname] = useState("Fury");

	return (
		<UserDestructuredContext.Provider
			firstname={firstname}
			lastname={lastname}
			setFirstname={setFirstname}
			setLastname={setLastname}
		>
			<ChildComponent />
		</UserDestructuredContext.Provider>
	);
};

Consuming dynamic context:

const DescendantComponent = () => {
	const {
		firstname,
		lastname,
		setFirstname,
		setLastname,
	} = UserContext.useValue();

	return (
		<div>
			Hello {firstname} {lastname}
		</div>
	);
};

If you do not need whole context, you can optimize effortesly:

const DescendantComponent = () => {
	const firstname = UserContext.useSelector(val => val.firstname, []); // the array is for dependency list. For most use-cases You can omit it.

	const [minLength, setMinLength] = useState(5);
	// if minLength changes, the passed function will be called anyway to ensure consistent data
	const isLastnameLongEnough = UserContext.useSelector(
		val => val.lastname >= minLength,
		[minLength]
	);

	return <div>Hello {firstname}</div>;
};

You can take optimization even further and pass equality function in order to avoid extra re-renders

const DescendantComponent = () => {
	const { firstname, setFirstname } = UserContext.useSelector(
		val => ({ firstname: val.firstname, setFirstname: val.setFirstname }),
		(prev, next) =>
			prev.firstname === next.firstname &&
			prev.setFirstname === next.setFirstname,
		[]
	);

	return <div>Hello {firstname}</div>;
};

You can use specific property or properties

const DescendantComponent = () => {
	const { firstname, setFirstname } = UserContext.useProperties("firstname", "setFirstname");
	const lastname = UserContext.useProperty("lastname");

	return <div>Hello {firstname} {lastname}</div>;
};

You can add internal contexts without explicitly passing values

const FirstnameContext = UserContext.addInternalContext(val => {
	return useMemo( // for optimization
		() => ({ firstname: val.firstname, setFirstname: val.firstname }),
		[val.firstname, val.firstname]
	);
});


// Since we are using UserContext in parent component, there is no need to use provider of FirstnameContext. the value will be provided internally.
// Note that FirstnameContext is an instance of DynamicContext and has every property that UserContext has.


const DescendantComponent = () => {
	const { firstname, setFirstname } = FirstnameContext.useValue();

	return <div>Hello {firstname}</div>;
};

Stacked Context

TL; DR: allows taking into consideration ancestor providers instead of only the closest one.

Creating StackedContext context:

import { StackedContext } from "react-flexible-contexts";

const StackedUser = StackedContext.create<Value>(); // you can pass default value too but it is not required
const UserRootProvider = StackedUser.context.Provider;

const UserMiddleProvider = StackedUser.addProvider(
	(current: Partial<Value>, prev: Value) => ({ ...prev, ...current })
);

const ParentComponent = () => {
	const [firstname, setFirstname] = useState("Nick");
	const [lastname, setLastname] = useState("Fury");

	return (
		<UserRootProvider
			value={{ firstname, lastname, setFirstname, setLastname }}
		>
			<UserMiddleProvider value={{ firstname: firstname + "!" }}>
				<ChildComponent />
			</UserMiddleProvider>
		</UserRootProvider>
	);
};

Readme

Keywords

none

Package Sidebar

Install

npm i react-flexible-contexts

Weekly Downloads

1

Version

0.0.15

License

MIT

Unpacked Size

37.2 kB

Total Files

16

Last publish

Collaborators

  • thomasmikava