2

I generate cards on a button press. These cards have a randomly generated number between 0 and 100. I am trying to setup a component or function that would allow me to sort all of these cards in numerical order ascending or descending, or both, when I click the sort button. I have tried the following code. Keep in mind, this is all contained within the same App component.

The random numbers are generated in the add card component.

I feel like I'm super close, but I can't figure out what I'm missing.


  const sortTypes = {
    up: {
      class: "sort-up",
      fn: (a, b) => a.number - b.number,
    },

    down: {
      class: "sort-down",
      fn: (a, b) => b.number - a.number,
    },

    default: {
      class: "sort",
      fn: (a, b) => a,
    },
  };

  const sortAll = () => {
    state = {
      currentSort: 'default'
    };

    onSortChange = () => {
      const { currentSort } = this.state;
      let nextSort;

      if (currentSort === 'down') nextSort = 'up';
      else if (currentSort === 'up') nextSort = 'default';
      else if (currentSort === 'default') nextSort = 'down';

      this.setState({
        currentSort: nextSort
      });
    };

    };
  

  return (
    <body>
      <header>
        <div className="ui buttons">
          <button type="button" onClick={addCard} className="ui button mb-1 mt-1 mr-1"><i className="plus icon"></i>Add Card</button>
          <div className="or mb-1 mt-1"></div>
          <button type="button" onClick={sortAll} className="ui positive button mb-1 mt-1 mr-1"><i className="redo icon"></i>Sort All</button>
        </div>
      </header>

  • 3
    Would be awesome if you could Sandbox this. – codemonkey Mar 02 '21 at 06:19
  • 1
    `sortAll` sort of looks like a react component, it's not named like one but it has state... but you are attaching as a callback? Where is `onSortChange` called to "select" the sort function you want to use? Where do you apply any sort function? – Drew Reese Mar 02 '21 at 06:23
  • https://codesandbox.io/s/exciting-mirzakhani-nw5ow?file=/src/index.js –  Mar 02 '21 at 06:48
  • There's a link to the codesandbox. The onSortChange is on line 72. The sort function should be applied on line 94. –  Mar 02 '21 at 06:49
  • I feel like I started doing it correctly but I've never done anything like this in React so I wasn't exactly sure where everything needed to go. –  Mar 02 '21 at 06:51

1 Answers1

0

Issues

  1. Functional components are "instanceless" so there is no defined this to reference.
  2. onSortChange is the button onClick handler you want to use to update the current sort.

Solution

  1. Move the currentSort to component state in a useState hook.

    const [currentSort, setCurrentSort] = useState("default");
    
  2. Fix the onSortChange handler to correctly update state.

    const onSortChange = () => {
      let nextSort;
    
      if (currentSort === "down") nextSort = "up";
      else if (currentSort === "up") nextSort = "default";
      else if (currentSort === "default") nextSort = "down";
    
      setCurrentSort(nextSort);
    };
    
  3. Use an in-line "sort" in the render return. Remember that array.prototype.sort is an in-place sort, i.e. it mutates the array. To get around accidentally mutating state first copy the array.

    {cards
      .slice() // <-- copy
      .sort(sortTypes[currentSort].fn) // <-- select sort function
      .map((cardNumber, index) => (
        <MainCard
          number={cardNumber.number}
          key={cardNumber.id}
          onRemove={() => removeCard(cardNumber.id)}
        />
      ))}
    
  4. Attach correct handler to button

    <button
      type="button"
      onClick={onSortChange}
      className="ui positive button mb-1 mt-1 mr-1"
    >
      <i className="redo icon"></i>Sort All
    </button>
    

Demo

Edit how-can-i-sort-a-list-of-cards-with-randomly-generated-numbers-inside-of-them

Full Code:

const generateId = (seed = 0) => () => seed++;

const getRandomNumber = function (min, max) {
  let getRandom = Math.floor(Math.random() * max + min);
  return getRandom;
};

const sortTypes = {
  up: {
    class: "sort-up",
    fn: (a, b) => a.number - b.number
  },

  down: {
    class: "sort-down",
    fn: (a, b) => b.number - a.number
  },

  default: {
    class: "sort",
    fn: (a, b) => a
  }
};

const MainCard = ({ number, onRemove }) => {
  return (
    <div className="card">
      <button
        onClick={onRemove}
        className="ui mini red basic icon button"
        style={{
          position: "absolute",
          top: "0",
          right: "0"
        }}
      >
        X
      </button>
      {number}
    </div>
  );
};

export default function App() {
  const [cards, setCards] = useState([]);
  const [currentSort, setCurrentSort] = useState("default");

  const addCard = () => {
    setCards((cards) => [
      ...cards,
      {
        id: generateId(),
        number: getRandomNumber(0, 101)
      }
    ]);
  };

  const removeCard = (id) => {
    setCards((cards) => cards.filter((el) => el.id !== id));
  };

  const onSortChange = () => {
    let nextSort;

    if (currentSort === "down") nextSort = "up";
    else if (currentSort === "up") nextSort = "default";
    else if (currentSort === "default") nextSort = "down";

    setCurrentSort(nextSort);
  };

  return (
    <body>
      <header>
        <div className="ui buttons">
          <button
            type="button"
            onClick={addCard}
            className="ui button mb-1 mt-1 mr-1"
          >
            <i className="plus icon"></i>Add Card
          </button>
          <div className="or mb-1 mt-1"></div>
          <button
            type="button"
            onClick={onSortChange}
            className="ui positive button mb-1 mt-1 mr-1"
          >
            <i className="redo icon"></i>Sort All
          </button>
        </div>
      </header>

      <div className="card-container">
        {cards
          .slice()
          .sort(sortTypes[currentSort].fn)
          .map((cardNumber) => (
            <MainCard
              number={cardNumber.number}
              key={cardNumber.id}
              onRemove={() => removeCard(cardNumber.id)}
            />
          ))}
      </div>

      <aside className="showHide"></aside>

      <footer>
        <h3 className="text-center text-muted">Footer</h3>
      </footer>
    </body>
  );
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181