Reputation: 2126
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?
Upvotes: 9
Views: 12389
Reputation: 3070
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).
Upvotes: 4