react-flexible-contexts
v0.0.15
Published
The goal of this project is to increase flexibility of native context API in react.
Readme
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>
);
};
