3
\$\begingroup\$

I have a React/Redux/Router/Reselect app in which I am syncing some parts of the redux state with the URL. As an interim, hacky measure (I've only been learning how to write wepapps for about 6 months so I'm very much a n00b right now) I’m currently implementing that sync as follows:

A selector provides the parts of the state which deviate from the default (e.g. the tab has changed from default and the filter bar is open and one of the filters has been changed from the default):

export const getStateConfiguration = createSelector(
  [getAllActiveFilters, getFilterBarVisible, getSelectedChart, getCurrentTab],
  (filters, filterBarVisible, selectedChart, currentTab) => {
    // inspect filters to create for deviations to default and return a config object with the stuff i need to write to the URI
     return config;
   }
);

A useEffect() in App.js is listening to that selector and when it changes calls a function in which JSON stringifies the selector's result to history. (I get that history object from router, because if I use uesHistory() from, say, BrowserRouter, I can't seem to escape infinite loop dependencies in useEffect().)

useEffect(() => {
  // some lines omitted for brevity
  writeToHistory(currentJsonConfig, stateConfiguration);
}, [dispatch, dbStatus, currentJsonConfig, decodedPathname, stateConfiguration]);

At app startup, another useEffect() in App.js calls a function which inspects the location and passes that to initialise the Redux reducers.

useEffect(() => {
    // some lines omitted for brevity
    dispatch(initialise(config));
  }
}, [dispatch, dbStatus, currentJsonConfig, location]);

As you can probably already see, all of this performs horrifically. Every time a single thing in state changes, the selector has to examine the whole state model, reparse it, provide that to useEffect(), which stringifies the whole thing again and writes it to history.

Due to performance and the hacky nature of my bespoke solution, I now plan to refactor it all to use a 3rd party library(s) which does all this for me. I’ve looked at things like use-query-params (https://github.com/pbeshai/use-query-params) and connected-router (https://www.npmjs.com/package/connected-react-router) but I was wondering if anyone has any experience, guidance, or opinions they can offer on the subject, for the most efficient, straightforward and current solution to the problem.

If it helps, you can see my source code here. The most relevant files which contain my hacky solutions are probably /App.js, /state/selectors/StateConfigurationSelector.js and /HistoryWriter.js. More generaly, if you do take a look and see any other n00b issues, do feel free to tell me!

Thanks :)

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

I ended up choosing the use-query-params library and writing a class which has several useEffects to sync state. One of the useEffects fires once at load, to update the redux state with the query params in the URL, and the others each hang off one aspect of the state and update the query params when that item changes. It works on the general rule that if the state element is not the default state, it should not add (or remove) the related query param, but if it is not default, then add the query param.

I couldn't just replace the relevant redux state with state in the query params for various reasons: 1) It's useful to keep state elements using the same pattern throughout, for modularity, and so that they benefit from redux dev tools; 2) the query params don't remember the state when the url changes; 3) the state does not care whether or not the item is default - it will always write it.

For those interested, my implementation is here: https://github.com/adybaby/taskmaster/blob/master/src/HistoryWriter.js and a demo here: https://master.d29jyoh6h48gjm.amplifyapp.com/

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.