React-and-RxJS/README.md

6.0 KiB

react-interview-q1

Summary

This was a coding challenge. To see the original prompt, scroll down to the "Original Prompt" heading. Nothing is edited below that. Above that, under the "My Implementation" heading, I will give some explanations as to why I made the choices I did. This was a really fun challenge because I actually used it as an opportunity to work outside of React's data model and leverage RxJS. This came with a couple fun edge cases to solve for and I am happy to nerd out about the solutions I came up with - Feel free to reach out!

Solution with slider on a small value Solution with slider on a medium value Solution with slider on a larger value

My Implementation

Styling Notes

  1. CSS follows in the footsteps of create-react-app and are external files pulled in via imports. There are no frameworks or libraries used for styling. Modern features like Flexbox and container queries are used to make the form responsive. Components are styled with component name prefixes to reduce the chance of class name collisions.
  2. I wanted to easily adjust the form components size when styling, so I created the ComponentViewer component to wrap the form. This provides a slider to adjust the width of the nested component (in this case Form) dynamically to see how it looks at different sizes without opening dev tools.

Component Composition Notes

  1. The job description this coding challenge was for listed RxJS as a requirement. I hadn't used RxJS before, so I used this as an opportunity to learn it. Because of this, I broke up the form into smaller components and chose not to pass data through props. Instead, I used RxJS Subjects to push data from the child components to anyone who would listen. In this case, the parent component, Form, listens to the NameInput and LocationInput components found in src/components/atoms.
  2. The RenderTable component is kept pretty simple and more closely follows idiomatic React. It receives data from the Form component and iterates through to render the table rows. There is also a minimum of 5 rows that will always be rendered. In order to achieve this though, key's are explicitly not provided so that React doesn't accidentally persist data when clearing. This is a trade off, but I think the user experience of seeing the table is more important than the minuscule performance loss. If this were populated with a large amount of data, I would reconsider this decision. While there would be a technical solution to achieve both I didn't want to sacrifice code readability for a small performance gain.

Just Cool Stuff

LocationInput

LocationInput is more like a Solid component in that once React renders it, there is no reason it needs to be rendered agin (YAY Performance!) and it relies on 'signals'. This is because the data is fetched from the API on mount and populated initially. Alongside this action, a listener is mounted so that whenever there is a change on the select element, it is dispatched straight to the Subject which makes the selection available to any other component outside of Reacts rendering cycle.

NameInput

NameInput is a little more complex. Similar to LocationInput, it pushes its state changes through a Subject making it available anywhere. Before we can do this though, we need to validate the input. Name validation is an async operation though and if we just validate every keystroke, we won't know if the response we get back is for the most recent keystroke. To solve this, I used an RxJS Operator to ignore all validation responses except the most recent. This eliminated the risk of the user seeing a false positive or negative caused by an older request coming back after a newer one.

This still has a massive problem though because the response to the most recent request could come back after the user has changed the input again. This is because the user could have typed in a new character and we are still waiting for that response to be received. There is no way to verify that the response is for the current string in the input box. The API is not available for us to make changes to as well so I solved this by wrapping every request in a new promise that includes the name we asked to validate. This way, we can save the name in a ref that is kept in sync with the input box and compare that with the validation response. If they are the same, we know the user has stopped typing and we can dispatch the valid name to the subject.

To make this a little better of a user experience, and because all the building blocks were there, I added additional states for validation. Every time a new key is pressed a pending is dispatched to the subject and a message is shown to the user. If the response comes back and the name is the same as the current input, the message is updated to reflect the validity of the name and an additional message is dispatched to the subject to inform other components that the name is valid or not.

This is actually consumed by the Form component to enable or disable the submit button only when the name is guaranteed to be valid. (using the Mock API of course, no way to solve the two generals problem here)

Original Prompt

Instructions

Fork this repo first into your own github account. Make sure to thoroughly read the instructions and implement the react component to meet the provided requirements. Send back a link to your cloned repo. You are expected to make implementation choices around customer experience and efficiency. Please make sure to explain your choices in comments.

Requirements

Please build the following form component form component mock

  • Name input should be validated using the provided mock API to check whether the chosen name is taken or not.
  • Name input should be validated as the user is typing.
  • Location dropdown options should be fetched using the provided mock API.
  • Component should have a responsive layout
  • Component should be appropriately styled
  • Unit tests are not required