Skip to content

Show and tell : case for using travels in a react app over use-travel #7

Description

@seedydocloop

Summary

Hey @unadlib 👋

First, thanks a million for this library !

I've implemented an undo/redo feature (in a react app) with complex fields, where each field's state has to be undoable and depends on several metadata.

To ensure my undo/redo implementation relies only on my frontend, I have to store a complex object in my history stack.

Therefore the fact that travels relies on JSON patches makes a lot of sense to me, performance-wise 🙏

I noticed I had a hard time using the use-travel hook for my needs, mainly because I didn't need the reactivity layer you brought there.

I ended up using travels over use-travel.
Therefore, I wanted to share this case for your knowledge, as a thank you for your work.

Hoping it may bring some food for thoughts 👍

Tech ecosystem

  • react app
  • react-hook-form as a form manager
  • travels for undo/redo

I think any app with form management should avoid having 2 sources of truth for their data, therefore, it is likely travels will be there only to safely store changes.

Why use-travel didn't answer my need

Reactive value not needed

Because I already had a form manager integrated, travels only comes as a layer on top of my data.

My app does not consume the state provided by travels/use-travel as a reactive value.

Binding to user interaction

I bind travels forward and back navigations to user interaction, then immediately propagate a state update to my form manager.

Therefore, the user interaction callback is the only time I need to consume travels state :

  const travelOnKeyDown = (event: KeyboardEvent) => {
      if (!travelsRef.current) return;
      if (isUndoKeyboardEvent(event)) {
        event.preventDefault();
        if (!travelsRef.current.canBack()) return;
        travelsRef.current.back();
        onChange(travelsRef.current.getState()); // react-hook-form binding
        return;
      }
      if (isRedoKeyboardEvent(event)) {
        event.preventDefault();
        if (!travelsRef.current.canForward()) return;
        travelsRef.current.forward();
        onChange(travelsRef.current.getState()); // react-hook-form binding
        return;
      }
    };

Imperative API over watchers

Because travels brings an imperative API, it makes for a simpler implementation for my use case.

As you can see in the previous example I shared :

        if (!travelsRef.current.canBack()) return;
        travelsRef.current.back(); // instantly navigates to previous state
        onChange(travelsRef.current.getState()); // this is the expected previous state

Having an imperative API allows me to navigate inside travel history then get the updated state, in sync.

With use-travel, I would have to split the navigation and the state propagation, because state wouldn't be updated from within my callback loop.

        if (!travelsRef.current.canBack()) return;
        travelsRef.current.back(); // navigates to previous state
        onChange(state); // state is a stale value

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions