Junior React Questions

headshot of author of blog post
Matt Davis
Posted in:
React
Junior Interview Questions

I am working through multiple simple React questions.

Hover Counter


The first one I will speak about is creating a simple counter button except when you hover the button it will increment the count.

1import { useState } from "react";
2
3const App = () => {
4  // Edit this component
5  const [count, setCount] = useState(0);
6
7  const handleHover = () => {
8    setCount((prev) => prev + 1);
9  };
10
11  return (
12    <div>
13      <button data-testid="button" onMouseOver={handleHover}>
14        Hover Me
15      </button>
16      <h1 data-testid="count">{count}</h1>
17    </div>
18  );
19};
20
21export default App;

The 'testid' is in regards to the site where I found this test.

reacterry

A very simple counter-example. With the added twist of using the `onMouseOver` or `onMouseEnter` property. In react it is important to remember to use the callback async version of state to always use the previous value when you are modifying state.

Why would you do that? Well, the state might be stale. This is only needed if the current state depends on the previous state of course.

The next one I will talk about is a bit more complex, and I actually had not the easiest time sorting this one out.

Transfer List

Reference: Web Dev Interviews

1Welcome to the META React JS interview.
2
3Your task is to build a transfer list component.
4
5A transfer list allows users to pass items from one list to another.
6UX has provided a mock up.
7
8The transfer list should consists of two lists with directional actions buttons.
9The user should be able to select one or multiple items and move the item 
10to the other list by clicking the action buttons.

To save you some trouble I will simply link my source code.

Of course, I will talk about how I approached the problem!

The first step was to actually figure out what the heck is going on. So we need a List component, we need a checkbox component and we need a button.

So that is what I did first. Created 3 functional components. List, Checkbox, Button. I am sure there is a better way here, as the Button might not be needed, but to keep things more reusable I allowed a `label` and `id` to be modified via props. The CheckBox might not really be needed either as it creates an added level of hierarchy when passing my functions down from `App`.

Let me explain.

At the app level, we need to keep track of which items are in which list. The Checkboxes naturally maintain state due do the fact that they are `checked`. However how to we keep track of that? We could've used state, but I think a better way is to use a flag on the items themselves.

1const items = [
2  { value: 1, isChecked: false },
3  { value: 2, isChecked: false },
4  { value: 3, isChecked: false },
5  { value: 4, isChecked: false },
6];

Beautiful. That is the initial state of the list on left! So we can send this list to our `leftList` and render it...

1...
2return (
3    <div className="appContainer">
4      <List list={listLeft} />
5    </div>
6  );
7...
8
9function List({ list }) {
10  return (
11    <div className="listContainer">
12      <div className="checkbox-form">
13      {list.map((item, i) => {
14            return (
15            // map over list and render checkboxes for each item
16            );
17          })
18        )}
19      </div>
20    </div>
21  );
22}

Please remebber this is an annotated version to explain the logic, to see the complete work head to Github.

Now we have a list, and inside the list we render Checkbox.

1function CheckBox({ checkboxId, index, checked, list, handleCheckboxSelect }) {
2  return (
3    <div className="checkbox-label">
4      <input
5        type="checkbox"
6        onChange={() => handleCheckboxSelect(list, index)}
7        id={`${checkboxId}`}
8        name={`${checkboxId}`}
9        checked={checked}
10      />
11      <label for={checkboxId}>{checkboxId}</label>
12    </div>
13  );
14}

At this point we haven't introduced what any of this is doing really. So lets take a step back. We want to have each checkbox's value `checked` toggled based on the App level knowledge of state. So we need to pass down a function and the current value for checked from the App through the list and into the Checkbox.

1  const handleCheckboxSelect = (list, index) => {
2    const item = list[index];
3    const value = !item.isChecked;
4    item.isChecked = value;
5   
6    setListLeft([...listLeft]);
7    setListRight([...listRight]);
8  };
9
10...
11
12
13function List({ list, handleCheckboxSelect }) {
14  return (
15    <div className="listContainer">
16      <div className="checkbox-form">
17        {list.length !== 0 ? (
18          list.map((item, i) => {
19            return (
20              <CheckBox
21                handleCheckboxSelect={handleCheckboxSelect}
22                key={i}
23                checkboxId={item.value}
24                index={i}
25                list={list}
26                checked={item.isChecked}
27              />
28            );
29          })
30        ) : (
31          <></>
32        )}
33      </div>
34    </div>
35  );
36}

We already know what the list is, and now we know what handleCheckBoxSelect does. We simply modify the items directly and then update each list respectively.

Ok Great! But we haven't moved anything yet, here is where our button comes in.

1function Button({ direction, handleButtonClick }) {
2  const buttonLabel = direction === "left" ? `<` : `>`;
3
4  return (
5    <button
6      type="submit"
7      id={`move-${direction}`}
8      value={direction}
9      onClick={(e) => handleButtonClick(e)}
10    >
11      {buttonLabel}
12    </button>
13  );
14}

And now from the App level!

1  return (
2    <div className="appContainer">
3      <List list={listLeft} handleCheckboxSelect={handleCheckboxSelect} />
4      <div className="button-container">
5        <Button handleButtonClick={handleButtonClick} direction="left" />
6        <Button handleButtonClick={handleButtonClick} direction="right" />
7      </div>
8      <List list={listRight} handleCheckboxSelect={handleCheckboxSelect} />
9    </div>
10  );
11}

But what about these functions? Well They need to do different hings depending on the direction. The process goes like this.

If the move is to the right.

1. Filter checkBoxes to move and to not move. (using the true/false flag)

2. Set the RIGHT list to be whatever the prev was plus the boxes to move

3. Set the LEFT list to be whatever we haven't moved.

Because we have changed the state the component will re-render.

1 const moveListRight = (list) => {
2    const dontMoveBoxes = list.filter((item) => !item.isChecked);
3    const moveBoxes = list.filter((item) => item.isChecked);
4    setListRight((prev) => [...prev, ...moveBoxes]);
5    setListLeft(() => dontMoveBoxes);
6  };
7
8// moveListLeft is the same just using the opposite state setters
9
10...
11

I have no doubt I have some bugs here, this is just my solution to this problem. If you have a better idea let me know!

A lot of interesting concepts in this one. I am sure I could clean this up but not using a new component for Button. I think using a state change and flags on the items is the simplest way, however, this could be done with the local state as well. What do you think?

 Back