@theuiteam/continuous-container
v2.0.1
Published
State container with the known past, present, and the future
Readme
Idea
This is almost react transition group, but for state management...
Why people react transition group? Because it 1) takes a pause between steps letting classNames be applied and 2) keeps children after you remove them, to perform a fade animation.
We are doing the same, but using state.
It's all about tracking what the value would be, what is right now, and what it was.
We call this ContinuousState
ContinuousState
future- the next value. The value you just set.present- to be synchronized with thefuture"later"past- to be synchronized with thepresent"later"
and
defined- an indication that any of future, past or present are truthy.
API
useContinuousState(value, options)
value- a value to assign to thefutureoptionsdelayPresent- time in ms betweenfuturebecomingpresentdelayPast- time in ms betweenpresentbecomingpast. For transitions, it usually equals to exist animation durationinitialValue- a value to be set as initial to thepastandpresent.futureis always equal to thevaluegiven as a first arg
useScatteredContinuousState(value, options)
Call signature is equal to useContinuousState, returns an object with extra property DefinePresent. See example below.
Usage
Problem statement
Let's imagine you have a switch. Which controls visibility of something, but you also want to add some animation.
Let's handle these cases separately:
const App = () => {
const [on, setOn] = useState(false);
return (
<div>
<button onClick={() => setOn(on => !on)}>Toggle</button>
// would be instanly hidden and shown
{on && <Content/>}
// would be animated, but would be ALWAYS rendered
<ContentWithAnimation visible={on}/>}
</div>
);
};Now let's imagine you want to not render Content when it's not visible and not required.
Ok, "when is this when"?
- render
ContentWithAnimationwhen it is about to be displayed - render
ContentWithAnimationwhen it is displayed - render
ContentWithAnimationwhen it is no longer visible, but still animating toward hidden state
import { ContinuousContainer, useContinuousState } from '@theuiteam/continuous-container';
const App = () => {
const [on, setOn] = useState(false);
const continuousState = useContinuousState(on);
return (
<div>
<button onClick={() => setOn((on) => !on)}>Toggle</button>
{/*render if any of past/preset/future is set to true*/}
{continuousState.defined && (
<ContentWithAnimation visible={continuousState.present} />
// wire the "present" state
)}
{/* or */}
<ContinuousContainer value={on} timeout={300}>
{
(past, present, future) => (past || present || future) && <ContentWithAnimation visible={present} />
// ^^ use the "present" value
}
</ContinuousContainer>
</div>
);
};Scattered
There are more sophisticated situations, when setting up something to display does not mean "display". Lazy loading is a good case
const App = () => {
const continuousState = useContinuousState(on);
return continuousState.defined && <LazyLoadedContentWithAnimation visible={continuousState.present} />;
};In such case ContinuousState will update from future to present before LazyLoadedContentWithAnimation component is
loaded, breaking a connection between states.
In order to handle this problem one might need to tap into rendering process using useScatteredContinuousState
const continuousState = useScatteredContinuousState(on);
return (
continuousState.defined && (
<Suspense>
<LazyLoadedContentWithAnimation visible={continuousState.present}>
<continuousState.DefinePresent />
{/*this component will advance ContinuousState once rendered*/}
</LazyLoadedContentWithAnimation>
</Suspense>
)
);For readability purposes we recommend putting DefinePresent to a separate slot different from children.
<LazyLoadedContentWithAnimation visible={continuousState.present} effector={<continuousState.DefinePresent />} />⚠️⚠️⚠️⚠️⚠️⚠️⚠️
The following code will NOT work as DefinePresent will be rendered instantly, even if suspense will be in fallback
<Suspense>
// will not be rendred until ready
<LazyLoadedContentWithAnimation visible={continuousState.present} />
// will be rendered too early
<continuousState.DefinePresent />
</Suspense>See also
- Phased Container from a
reconditionlibrary
License
MIT
