在React中,{Transitions} = F(state)。
In React {Transitions} = F(state)

原始链接: https://jordaneldredge.com/blog/transitions-f-of-state/

React 应用可以被视为状态机,其中UI和有效的用户操作(转换)是当前状态的函数: `{transitions} = f(state)`。React 组件根据状态定义UI,并通过在DOM中渲染的事件处理程序隐式地控制有效的用户操作。 异步更新,例如通过API删除待办事项,可能会破坏此模型。“完成”按钮在API调用期间可能仍然可点击,从而导致无效操作。为了防止这种情况,可以使用乐观更新(立即从UI中删除待办事项)或待处理状态(禁用按钮)。 并发React特性,例如`startTransition`,通过延迟UI更新进一步增加了复杂性。因此,任何影响有效操作的状态更新都应与同步的乐观更新或待处理更新配对,以防止无效的用户操作。React的并发特性通常提供对待处理标志或乐观更新的内置支持来应对这一挑战。使用这种思维模型将有助于阐明哪些更新可以安全地异步或并发应用。

Hacker News讨论总结: 文章“在React中 {Transitions} = F(state)”讨论了如何将React应用视为状态机的模型,其中UI是状态的函数 (UI = f(state))。然而,dfabulich认为,将复杂的应用正式地建模为状态机,特别是用图表表示,通常是不切实际的,且价值不大。状态机图在状态和转换数量超过一定程度后会变得笨拙且无用。React的优势在于将UI视为状态的纯函数,这比管理复杂的状态图更有效。这是一个更简单有效的模型。 dvtkrlbs补充道,在真实的React应用中,组件经常依赖于上下文和第三方资源,使得纯粹的UI = f(state)范式并不总是直接适用,尽管对于实际的视图组件来说,这是一个有益的方法。
相关文章
  • (评论) 2024-07-30
  • (评论) 2025-03-27
  • (评论) 2024-08-21
  • 同步预算 2025-03-27
  • (评论) 2025-03-20

  • 原文

    TL;DR: Thinking about the React component tree as modeling a state machine can help clarify the implications of asynchronous updates and React’s concurrent features.


    A state machine is a formal way of describing a stateful system which changes over time. It generally consists of explicitly defining the states that the system can be in, as well as defining a state transition table which enumerates the set of valid transitions (updates which put the machine in a new state) for each discrete state.

    A React application can be thought of as modeling a state machine. Each render takes a state and produces the UI for that state. This is the famous UI = f(state) mental model of React. But, for complex applications, formally enumerating a transition table is often not feasible. When the number of possible states is not finite, a table will not suffice and we must instead define a mapping: a conceptual function which takes in a state and returns the set of valid transitions for that state.

    If you look closely, each React application actually already defines a version of this function. Developers implicitly define the set of transitions that are valid for a user to take in each state by the event handlers that their components bind into the DOM when rendering that state.

    We can say that the React component tree not only defines UI as a function of state, but also defines the set of valid transitions for the state with that same function. More succinctly:

    {transitions} = f(state)

    For example

    A Todo app might support a state update complete(todoId) which marks a todo as completed. It would be an error for a user to “complete” a todo which has previously been deleted. At best it would result in showing the user an error popup, and at worst would result in a “TypeError: Cannot set properties of undefined” JavaScript error. In state machine terms, complete(x) is not a valid transition for a state that does not include a todo with the id x.

    The React programming model gives us an intuitive way to guard against this type of error. So intuitive, that we often don’t even think about needing to guard against these kinds of errors. If the only way to trigger the complete(todoId) update is from the “Complete” button in the <Todo /> component, simply removing a todo from our state when it’s deleted automatically ensures we never render a “Complete” button for a deleted todo, and that ensures the user cannot trigger this invalid update.

    Note that the above depends upon JavaScript being single-threaded with a blocking UI. When we update the state of the application synchronously, we can trust that before the browser yields to more user input, it will first flush these state changes to the UI.

    Asynchronous updates

    With asynchronous updates, we no-longer get this guard for free. Consider the case where deleting a todo requires making a network request to an API. If a user deletes a todo, and we wait to update our application state until after that network request completes, there will be a period of time when the deleted todo’s “Complete” button is still rendered on the page and clickable, even though it’s no-longer a valid update for the user to perform.

    Preventing the user from performing an invalid update no longer happens automatically. Instead it requires some additional work, but we have two good options:

    1. Optimistically delete the todo from our local state ensuring the todo is not rendered at all while the network request is in flight.

    2. Mark the todo as “pending” in our state while the network request is in flight and disable the “Complete” button while a todo is pending.

    If we flush this optimistic or pending state update synchronously, {transitions} = f(state) ensures we’ve prevented the user from being able to trigger the invalid update.

    Concurrent updates

    Some of React’s concurrent mode features, for example startTransition, allow you to perform state updates which intentionally do not flush to the DOM before yielding to the user. Similar to asynchronous updates this leaves a window of time where the UI may allow the user to trigger an update which we know to be invalid.

    This means that if we perform a state update which will change which updates are valid, we will need to pair it with an optimistic or pending update which flushes synchronously.

    For this reason, many of React’s concurrent features include built-in support for either an isPending flag (useTransition, useActionState, useFormStatus) or an optimistic update that is paired with the low priority update (useOptimistic).

    Conclusion

    {transitions} = f(state) is a useful mental model for thinking about how your application guards against letting users trigger invalid updates. It helps clarify which updates are safe to apply asynchronously or concurrently, and which updates need to be paired with a synchronous update in order to guard against letting the user trigger invalid actions. It also helps clarify the role that your React components play in enforcing these guards, such as which components will need to render differently when a piece of state is in a pending state.


    Thanks to Evan Yeung for the conversations which lead to this observation and to Joe Savona, Jordan Brown, Jack Pope and Rick Hanlon for reading early drafts of this post.

    联系我们 contact @ memedata.com