-
-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance/differences compared with redux #50
Comments
I see what you mean, very good finding. Now, I needed to change the logic in v1.3.0, to avoid a React warning that is introduced in 16.13. My plan is to move to v2 #43 once React releases a new version with useMutableSource. Lastly, while a render function runs, it will not be committed unless state is changed. It means if a render function is computationally heavy, the performance may drop a lot. Sorry for the inconvenience. Unless you need to stick with a specific React version, it should be ok to keep using this lib and wait for the future. |
Very quick reply thank you, for my particular usecase, i'm trying to make sure I nail the performance early on (looking at building a way of building complex nested layouts with drag and drop, so keen to make it as optimised as possible). Its more of a project at the moment, so isn't a big rush, is this something you feel will be behaving in the way described after v2 is released? Anything stopping me from using v2 at the moment? In terms of a performance drop, I believe majority of the cost i'd have is rendering charts for example, which wouldn't be an issue here, however if I had to manipulate data heavily to render the charts (lots of loops or alot of data), I could see this being problematic. Thanks again for your reply, I'm particularly keen on this since I don't want to be tied to redux, worth noting if theres anything I can help with, let me know :) |
v2 is already alpha released: https://www.npmjs.com/package/react-tracked/v/2.0.0-alpha.4 You can try v2-alpha to see how it is going to be like. I actually encourage you for this trial, because that could reveal some bugs I don't notice now. This is not for production. Feel free to ask here, if you have some issues with trying v2. |
I've just had a try on one of the examples (examples:01_minimal) on the v2 branch, and still see the same thing (changing the textbox for example also fires console.logs inside the counter) |
OK, I just tried and confirmed the latest v2 branch, which doesn't work. Anyway, I understand the behavior. As you observed, it triggers an extra render function. If you change Meanwhile, let me run js-framework-benchmark. |
Is it correct, that the current stable version of the library doesn't prevent all unnecessary re-renders of the components? I use React Context within my app with useReducer. It was quite clean way to replace native React Context provider with this library. But in the profiler, i don't see any changes in rendering of the components. Everything seems to work stable, but all component tree seems to be re-rendered as it does with React Context usage. |
@ShamansCoding I understand the latest stable version (v1.4.1) doesn't seem to behave intuitively. It may call the render function but will not commit. So, you might want to compare the behavior with useEffect. useEffect(() => {
console.log('rendered');
}); If you use v1.2.0 and it works as expected, my assumption should be correct.
I'm not sure what this means. There might still be room for improvements in v1.4.1. If you could share a codesandbox, I can investigate it. Thanks! |
Thanks for the quick response! I meant React dev tools extension. And its option "Highlight updates when components render." I've tried to use v1.2 and v1.4.1 and the result is the same. Then I trigger any changes in the application general context store(which is replaced by the library), all components shown as re-rendered. Even those components that don't use part of the store that has changed. I also placed some console logs inside re-rendered components and they show the same picture. Below is what I've done. Code before: import React, { createContext, useReducer } from 'react';
import { userInitialState, userActions } from 'actions/userActions';
import { drawerInitialState, drawerActions } from 'actions/drawerActions';
const initialState = {
...userInitialState,
...drawerInitialState,
};
const Actions = {
...userActions,
...drawerActions,
};
function reducer(store, action) {
const selectedAction = Actions[action.type];
const { payload } = action;
const update = selectedAction({ store, payload });
return { ...store, ...update };
}
const initialContext = { store: initialState, dispatch: null };
const StoreContext = createContext({ ...initialContext });
function StoreProvider({ children }) {
const [initialStore, dispatch] = useReducer(reducer, initialState);
return (
<StoreContext.Provider value={{ initialStore, dispatch }}>
{children}
</StoreContext.Provider>
);
}
export { StoreContext, StoreProvider }; Code after: import React, { createContext, useReducer } from 'react';
import { createContainer } from 'react-tracked';
import { userInitialState, userActions } from 'actions/userActions';
import { drawerInitialState, drawerActions } from 'actions/drawerActions';
const initialState = {
...userInitialState,
...drawerInitialState,
};
const Actions = {
...userActions,
...drawerActions,
};
function reducer(store, action) {
const selectedAction = Actions[action.type];
const { payload } = action;
const update = selectedAction({ store, payload });
return { ...store, ...update };
}
const useValue = () => useReducer(reducer, initialState);
export const {
Provider: StoreProvider,
useTrackedState: useStore,
useUpdate: useDispatch,
} = createContainer(useValue); |
Oh, I see. Yeah, I noticed it before too. TBH, I don't know how it works.
Yeah. I'm pretty sure it's not our issue. It's either the dev tools doesn't take the hack (changedBits = 0) into account, or it highlights all context consumers regardless of it.
Are you sure? Try it with v1.2.0. With v1.4.1, please use |
@dai-shi Besides the fact that the semantics of
From here, I assume React's commit/reconciliation process somehow discards output from CompA after diffing before/after outputs. Moreover, I'm trying to keep components as small as I can, especially state-consumers ones. So far the performance impact is not that much of an issue. I guess it's just an implication of this |
Well, thanks for asking! (I'd keep to describe v1.2.0 behavior.)
This is not correct. Because only Y is changed, CompA will not be executed. Hense, no reconciliation work is done by React.
If you split contexts too, it's good. You don't need any global state solution.
I'm not so sure how class components and function components differ in this case. (I'm not so sure if I can answer well your to your question, so follow-ups are welcome.) |
I looked more carefully for my case, and I found out what is happening. The re-renders were caused because the direct parent of the component is using a part of the store which is changed. So all child components get affected by this re-render. Then I change the part of the state, which is not used by the parent component, there is no additional re-renders. Both in v1.2.0 and v1.4.1. When a parent is using the part of the state, re-render is triggered for all children. |
Thanks for your investigation.
Yeah, that's React default behavior. It may confuse developers sometimes. |
Maybe related discussion: facebook/react#19200 |
@dai-shi which version is the best to choose i am using v1.4.2 , do you think is recommended to stay up to there or move to the latest one or v1.4.1 to avoid re - render? |
Short answerPlease use the latest version. If you find any issues, please report it. Long answer
|
Let me close this. Please file a new issue for a new discussion. |
I can provide an example if need be, but I've been looking at using your library rather than redux for a component I'm building. One major difference I notice is that, whenever any state changes, every single component in the tree does run the functional components (can see this by adding console logs within them, for all of your examples)
My current implementation using redux, and useSelector, only actually logs the components that have changed based on state. e.g. the textbox being updated, whereas switching to this, I actually see every single print a console log out, even if it is not impacted.
So is this a key difference? Would you expect the logic within components to still be ran? even if the rendering is optimised (Which i'd expect react to take care of anyway?)
The text was updated successfully, but these errors were encountered: