Frontend state & data flow rework
Reworks the way that Adatest's front end handles data caching and refreshes stale data.
Description of work
- Data fetched from backend is now stored in a Redux store, whereas previously it was cached in a custom class. Redux gives us nice debugging tools and an idiomatic way of working with React's rendering model.
- The frontend is now responsible for fetching data when it's stale, instead of receiving new data pushed proactively by the backend. This allows the client-side code to have much more control over when components re-render. There have previously been issues with the frontend re-rendering with the results of long-running operations (for example, computing model output), overwriting whatever the user was doing in the meantime.
- Every websocket message is now assigned a sequence number so we can track its flow. The backend is expected to respond with an 'ok' along with the corresponding sequence number if the operation worked. This will allow the frontend to respond to timeouts or other backend errors gracefully.
- TypeScript support added everywhere, mainly used for specifying the types stored in each component's props and state. This will make it easier for everyone to see exactly what data a component expects. I didn't enable strict mode, so hopefully it won't be too annoying for anyone who wants to write dynamically typed code. However, I did enable strict null checks, because it helped me fix several bugs that were lurking in the code base.
Implementation details
One slightly strange thing I had to do was figure out how to make our class-based components work with React's hooks, which can only work with function components. This was necessary because hooks have become the new standard for React libraries like Redux, and the new Redux Toolkit removes a lot of boilerplate previously required for Redux.
The general idea is to wrap a class component with a function component which is able to use hooks and pass on anything needed as props to the class component. In our case, the function components fetch data from the Redux store and pass it on as props for the class components, which allowed me to reuse almost all the logic inside the original class components.
The main files for Redux are store.ts and TestTreeSlice.ts.
Known issues
Things are mostly working, but there are still some bugs to fix. Work still in progress:
- ContentEditable is still buggy. I am still figuring out how to make it work gracefully with the new data flow.
- UI lag caused by aggressively refreshing data
- Code cleanup - this was a large change and I'd like to make it less disruptive if possible. There is also some code I've commented out that I should delete.
Here's the gist of the logic flow of how data is sent to the server, and how it's received and put in the Redux store.