-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
Feature Request: Global didUpdate() #10900
Comments
@ccorcos there is work being done on a top-level API for managing the render and commit phases. Check out this demo and explanation by @acdlite to get an idea of where things might be headed. |
Notion is pretty cool btw. Say hi to Surganov. :-) |
Interesting use case. Suggests that Redux is missing this feature, too. Something like |
I think the thing the only reason you would need a global The reason you want the callback to scheduled per action is that, in the future, once we move to async, different actions may be scheduled with different priorities. So callbacks may fire at different times depending on the priority. |
Slight tangent: I have this idealized, possible-future version of React Redux in my head where there is no "store" and all state is stored as component state inside the Provider. Something like this: class Provider extends React.Component {
state = { storeState: null };
dispatch = (action, callback) => {
this.setState(state => {
const nextStoreState = reducer(state.storeState, action);
return {storeState: nextStoreState};
}, callback);
});
// ...
} This would allow React's scheduler to abort, reorder, and rebase Redux actions according to their priority, which isn't possible today. Though of course we'd have to figure out what this means for middleware and enhancers. And maybe fix context (though that's not necessarily a blocker, |
Of course, then you don't really have "React-Redux" any more, you have "Redux in React" - which is not the same thing :) |
Don't you Redux-splain and React-splain at me, acemarke :) |
Thanks guys! @acdlite you touched on a few things there. The If I understand you correctly, what you were getting at is that the user's focus could be a state in redux and managed by the component itself. If you're living in Redux world, I can buy into that. But there are more complicated examples like clicking DOM elements and scrolling to elements after they appear that would be challenging to handle via state. These actions are one-off events and should probably live in the redux action. Async renderering and reprioritizing is an interesting dilemma. There could be an API like |
No, that's not what I meant to suggest. The point of the callback is to fire right after the action has been flushed to the DOM, which is what the second parameter to A future version of Redux could potentially expose this by adding a second argument to |
Ahhh. I see what you mean. Right now, I'm just waiting for all the As far as implementing a granular callback with setState inside Redux, especially in a pure way, that sounds challenging because you don't know which components need to update until they begin to re-render... I suppose that means the only way we can do this at a granular level is to implement our own render queues within the state management layer. But that doesn't mean we couldnt implement a global React.didUpdate that waits for all rendering to flush. |
When you call A global |
Want to make sure this part is clear: the only reason Redux triggers many |
@acdlite Has any research been done into coordinating More concretely, I'm thinking something like this: handleChange = event => this.batchedSetState(BATCH_ID, { foo: event.target.value }) That would group up units of work and only reconcile with the DOM after the VDOM has been fully-rendered for those components. That would be a way to ensure sync-like behavior when BTW, I saw some library recently that does what you describe as a future react-redux. I forget the name, but it's basically that. I'll try to find it again... |
Now I'm curious - if you've seen a lib, there's a good chance I've seen it, but nothing is immediately coming to mind. |
I only found freactal, but I'm pretty sure that's not it. |
@acdlite I see your point abut tearing -- that makes sense. And we'll need some way to ensure that two components render in sync -- that's what was going on in that demo. Pretty much every library that I've seen uses subscribers within components, otherwise it doesn't scale well. I could image libraries using custom render queues to enqueue forceUpdates and committing them all together. That way you could have multiple render queues and run animations on a separate queue that doesn't happen in sync. Cool stuff! Any ideas when this will land in production? This is sounding more and more like an external library feature and less like a React feature the more we talk about it... |
@timdorr Yeah we've considered something like that. Probably wouldn't use batch IDs; as you say, batching in React is done by priority level. But we're also moving to model where the priorities are expressed as expiration times. The lower the priority, the later the expiration time; as the current time advances, things increase in priority and eventually "expire," at which point the rest of the work is flushed synchronously. We group like-priority updates by rounding the expiration time. E.g. all low priority updates scheduled within ~30ms will have the same expiration time. But if you happen to start scheduling a whole bunch of updates right on the edge of a 30ms bucket, some could spill over into the next one. Doesn't even have to be a large amount of updates if you're right on the edge (say, 29.9ms). So if you think about how Redux works today, after an action is dispatched and the store updates its state, we call Another problem we'll have to solve with Redux is the lack of a commit phase, or synchronization phase. If someone calls This gets back to why we should store state in the Provider's component state and use An example. If you dispatch a normal priority action const initialState = {log: []};
function reducer(state, action) {
return {log: [...state.log, action.value]};
}
dispatch({value: 'A'}); // Normal pri
dispatch({value: 'B'}); // High pri
// First React flushes only B, because it has higher priority
// State should be {log: ['B']}
// Later, React flushes both A *and* B, in that order.
// State should be {log: ['A', 'B']}
// Note that we rebased the high pri action on top of the low pri action. Thanks, React! But this means getting rid of "stores" as we know them today, along with things like Another way of saying this is that React is already a really good, sophisticated system for scheduling UI state updates. Redux is a pattern on top of that, not something that replaces it. |
We’re trying to funnel API proposals into our RFC repo so please feel free to create a pull request there and we’ll review it: https://github.com/reactjs/rfcs I’ll close this one but we appreciate the discussion! |
Currently, its not easy to write global logic that executes after React has re-rendered. The
componentDidUpdate
lifecycle method works great when your logic is isolated to a component, but I've found myself more and more recently wanting a globaldidUpdate
hook baked into React.A simple example where this is useful is if you want an isolated function (perhaps a keyboard shortcut) that creates an element on the screen and then focuses it.
At Notion, we've written custom logic for doing this, but it makes upgrading with React more difficult and unstable. I think this would be useful for others too, particularly those who use Redux and are building complicated UI interactions.
The text was updated successfully, but these errors were encountered: