React storing Ref elements in Redux
Asked Answered
M

1

9

How would you go about storing Ref elements in Redux and would you do it at all?

I have a component containing some form elements where I need to store the state of which field in the form the user had selected if they leave the page and come back.

I tried registering each input field in Redux like so (I'm using the <InputGroup> component from Blueprint.js):

<InputGroup
  inputRef={(ref) => { dispatch(addRefToState(ref)) }}
  ...more props...
/>

That resulted in a circular JSON reference error since Redux is serializing the ref element to JSON in order to save it to localStorage. I then tried "safe" stringifying the object with a snippet I found here on Stackoverflow, removing any circular references before turning the object into JSON. That sort of works, but the Ref elements are still so huge that 3-5 refs stored in state turns into a 3MB localStorage and my browser starts being painfully slow. Further, I'm concerned whether I can actually use that Ref object to reference my components, know that I essentially modified the Ref object. I've not tried yet, because performance is so poor with the stringified objects.

I'm contemplating abandoning "the React way" and just adding unique IDs on each component, storing those in Redux and iterating over the DOM with document.querySelector to focus the right element when the page is loaded. But it feels like a hack. How would you go about doing this?

Mungovan answered 9/8, 2020 at 10:43 Comment(0)
M
4

I am not sure if I would use them for that purpose but It would not be among the first ways to do that.

It is perfectly fine to have a React state to store a unique identifier of focused form element. Every form element, or any element in general, can have a unique identifier which can just be a string. You can keep them in your app's redux store in any persistence like web storage.

While you navigate away you can commit that state to your redux store or to persistence, by using a React effect.

    const [lastFocusedElementId, setLastFocusedElementId] = useState();
    
    useEffect(() => {
      // here you can get last focused element id from props, or from directly storage etc, previously if any
      if(props.lastFocusedElID) {
        setLastFocusedElementId(props.lastFocusedElID);
      }
      // here in return you commit last focused id
      return saveLastFocusedElementID(lastFocusedElementId) // an action creator that saves to the redux store before every rerender of component and before unmount
    }, [props.lastFocusedElID]);

Alternatively

const [lastFocusedElementId, setLastFocusedElementId] = useState();

useEffect(() => {
  const lastFocusedElID = window.localStorage.getItem(lastFocusedElementId);
  if (lastFocusedElID) {
    setLastFocusedElementId(lastFocusedElID);
  }
  return window.localStorage.setItem('lastFocusedElementId', lastFocusedElementId);
}, []);

Not to mention you need to use onFocus on form elements you want to set the last focused element ID. Id can be on an attribute of your choice, it can be id attribute, or you can employ data-* attributes. I showed id and data-id here.

<input onFocus={e => setLastFocusedElementId(e.target.id)} />
<input onFocus={e => setLastFocusedElementId(e.dataset.id)} />

Also needed a way to focus the last focused element with the data from your choice of source when you reopen the page with that form elements. You can add autoFocus attribute every element like

<input autoFocus={"elementID"===lastFocusedElementId} />

Lastly if your user leave the form page without focusing any element you might like to set the id to a base value like empty string or so. In that case you need to use blur events with onBlur handler(s).

Mitsukomitt answered 10/8, 2020 at 11:39 Comment(3)
Thanks, that's very similar to the approach I was considering switching to. I'll see if I can get it to work.Mungovan
Do not hesitate, and if you are concerned about the way, it is perfectly React way, though there is nothing wrong about the contrary ways. Also above way will not require huge data sizes you have encountered before. Though i tmay require some tweaking, maybe there are some mistakes.Entrust
Refs are non serializable right? Why would you store a non serializable data on redux? That's not recommendedChangeover

© 2022 - 2024 — McMap. All rights reserved.