JSFiddle - React, Tailwind, and code Playground

[ASSIGNMENT] Close Frontend Application Quiz

by nathanlogan

HTML

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>

CSS

h2 {
  font-weight: bold;
  padding: 1rem;
}

.Selected {
  list-style: none;
  padding: 0 0 0 1rem;
  margin: 0 0 1rem;
}
.Selected__item {
  display: inline-block;
  margin-right: 0.5rem;
}
.Selected__item::after {
  content: ', ';
}
.Selected__item:last-child::after {
  content: '';
}

.List {
  margin: 16px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  grid-auto-rows: 60px;
  grid-gap: 16px;
}

.List__item {
  line-height: 60px;
  text-align: center;
  color: white;
  text-shadow: 1px 1px rgba(0, 0, 0, 0.5);
  box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.05);
}

.List__item:hover {
  opacity: 0.7;
  cursor: pointer;
}

.List__item-selected {
  outline: 3px dashed red;
  outline-offset: 1px;  
}

/* Taken from https://clrs.cc/ */

.List__item--navy {
  background-color: #001f3f;
}

.List__item--blue {
  background-color: #0074d9;
}

.List__item--aqua {
  background-color: #7fdbff;
}

.List__item--teal {
  background-color: #39cccc;
}

.List__item--olive {
  background-color: #3d9970;
}

.List__item--green {
  background-color: #2ecc40;
}

.List__item--lime {
  background-color: #01ff70;
}

.List__item--yellow {
  background-color: #ffdc00;
}

.List__item--orange {
  background-color: #ff851b;
}

.List__item--red {
  background-color: #ff4136;
}

.List__item--maroon {
  background-color: #85144b;
}

.List__item--fuchsia {
  background-color: #f012be;
}

.List__item--purple {
  background-color: #b10dc9;
}

.List__item--black {
  background-color: #111111;
}

.List__item--gray {
  background-color: #aaaaaa;
}

.List__item--silver {
  background-color: #dddddd;
}

React

const { Fragment, useState } = React;

// Implement a feature to allow item selection with the following requirements:
// 1. Clicking an item selects/unselects it.
// 2. Multiple items can be selected at a time.
// 3. Make sure to AVOID UNNECESSARY RE-RENDERS (performance).
// 4. Currently selected items should be visually highlighted.
// 5. Currently selected items' names should be shown at the top of the page.

// Note:
// Below shows what I think you're shooting for in avoiding re-rendering.  If the 
// requirements are to avoid absolutely all possible re-renders, you could architect 
// a solution that is based on each item maintaining its own internal state, and then 
// it could trigger a parent function that would keep a copy of cumulative changes.
// It would then only rerender the "Currently selected" list (as its own component).
// The effect would be that some app context holds the state, and only the pieces 
// that may change ever rerender.  Practically, though, I think the below gets you there.

const List = ({ items }) => {
	const [selected, setSelected] = useState([])
  
  const toggleSelected = (name) => {
  	let newlySelected = []
    
  	if (selected.includes(name)) newlySelected = selected.filter(n => n !== name)
    else newlySelected = [...selected, name]
    
    setSelected(newlySelected)
  }

	return (
    <Fragment>
      <h2>Currently selected items ({selected.length})</h2>
      <ul className="Selected">
        {selected.map(name => (
        	<li key={name} className="Selected__item">{name}</li>
        ))}
      </ul>
      
      <h2>All items:</h2>
      <ul className="List">
        {items.map(item => (
          <MemoizedItem
            key={item.name}
            color={item.color}
            selected={selected.includes(item.name)}
            onClick={() => toggleSelected(item.name)}
          >
            {item.name}
          </MemoizedItem>
        ))}
      </ul>
    </Fragment>
)};

const Item = ({color, selected,...