32
loading...
This website collects cookies to deliver better user experience
Box1
element selected, and want to change it backgroundColor
style by the color picker in the Details
component. Let describe some possible implementations in React:Application
component). We would pass to the Details
component the selected element, and a callback (updateComponent
) to modify it. Then on color selection this callback updateComponent
would be invoked, which would update state of Application
component. Afterwards Application
, Canvas
and Box1
components would be re-rendered and finally background color will be updated.useCallback
/ useEffect
/ useMemo
) to check if they need to update. Also re-rendering Canvas
would cause invalidation of properties of all boxes (need to check if incoming properties changed for this specific box). In real-world application you'll get even more dependencies to update (for sure Canvas
will not be the only child of Application
). Also this is positive scenario, which suppose that all memoization in your app is properly managed.Box1
on every mouse move to get a handy preview experience? I think in some cases it will still work, but at certain point you might reach performance wall, that will force you to optimise your application. And in this case simple implementation might become not so simple.react-redux
uses context under the hood as well). So when the state stored in context is updated, all dependent components are notified. useContext
re-renders when it's updated. This problem might be solved by fragmenting different parts of the state to different contexts. But I'd prefer application data to be separated in base of logical distinction, rather than in base of thinking how it will re-render less.useSelector
hook are notified, but than a selector
is run to extract selected state, afterwards it figures out, if that component actually need to be re-rendered. This mostly solves the re-rendering issue, but still, more components are subscribed to the store, more selector logic need to happen. useSelector
hook you need to reference full application state. Which means if your module consumes user data, it has to be aware that this user data is stored under user
key in the root state. Not good for modules decomposition. In general context (and especially with redux) makes it harder create reusable components, and bootstrap unit tests / storybook.const Application = () => {
const elements = createObserable([]);
return <Canvas elements={elements} />
}
const Box = ({ element }) => {
const [backgroundColor, setBackgroundColor] = useState(0);
useEffect(() => {
const unsubscribe = element.backgroundColor
.subscribe(value => {
setBackgroundColor(value);
});
return () => {
unsubscribe();
};
}, []);
return <div style={{ backgroundColor }} />;
}
Box
component function need re-execute. Suppose for example situation when component has more than one child. But what if we create an ObserverDiv
component, that will detect all observable properties automatically, then the code can be reduced to:const Box = ({ element }) => {
const { backgroundColor } = element;
return <ObserverDiv style={{ backgroundColor }} />;
};
backgroundColor
for one element only ObserverDiv
will be re-rendered and the rest of the app will remain untouched. Very similar to the context / redux approach, but without related concenrns.element
property (like element.backgroundColor
) observable. Here's where proxy enters in the game. Within a javascript proxy object you can override get
accessors, and return another proxy, which will create a lens to backgroundColor
, now you can directly subscribe to it.import Mlyn, { seal, useSubject, For } from "react-mlyn".
const Application = seal(() => {
const elements$ = useSubject([{
id: "some-random-id",
backgroundColor: "black",
}]);
return <Canvas elements$={elements$} />
});
const Canvas = seal(({ elements$ }) => {
return (
<For each={elements$} getKey={({ id }) => id}>
{(element$) => <Box element$={element$} />}
</For>
);
});
const Box = seal(({ element$ }) => {
const { backgroundColor } = element$;
return <Mlyn.div styles$={{ backgroundColor }} />;
});
backgroundColor
of an element, only the Mlyn.div
component will be re-rendered.