javascript – React : sorting components with multiple options

I have a list of components displayed in a table-like page. I want the user to be able to sort those components based on several elements. If a put a single toggle specified sort/default order, it works like a charm. But if I add several options to sort my components, the logic is kind of broken.

Default display :

When I sort alphabetically by name :
sorted alphabetically

When I sort the other way AFTER sorting alphabetically :
sorted the other way

As you can see, the content stays in place when it should move with the second sorting order. BUT, font colors move to the place where content should move. So certain information are right but others are not. And I don’t really know where to look to solve this.

What do I know:

  • Any sorting order works individually, so the problem is not here
  • components are supposed to be unmounted at the beginning of my function since I clear the state containing them

Bellow is my code. Parent component :

export default function VariablesManagement() {
(...)
  const [currentSort, setCurrentSort] = useState("default");

  return (
      <Dashboard containerClass="dashboardContainer variablesGlobalContainer">
        <article className="variablesHeader row">
          <MyButton
            buttonInnerText="scope"
            buttonSize="long"
            iconColor="altButtonColor col"
            buttonActionFunctionOne={setCurrentSort}
            buttonActionPropOne={
              currentSort === "scope" ? "scope-reverse" : "scope"
            }
            iconName={
              currentSort === "scope"
                ? "bi bi-sort-alpha-down-alt"
                : "bi bi-sort-alpha-down"
            }
          />
          <MyButton
            buttonInnerText="name"
            buttonSize="long"
            iconColor="altButtonColor col"
            buttonActionFunctionOne={setCurrentSort}
            buttonActionPropOne={
              currentSort === "name" ? "name-reverse" : "name"
            }
            iconName={
              currentSort === "name"
                ? "bi bi-sort-alpha-down-alt"
                : "bi bi-sort-alpha-down"
            }
          />
          <p className="col">value</p>
          <p className="col">format</p>
          <p className="col">description</p>
          <div className="variablePlaceholder" />
        </article>
        <VariablesBody
          allVariables={allVariables}
          currentSort={currentSort}
          setCurrentSort={setCurrentSort}
        />
      </Dashboard>
  );
}

VariablesBody:

export default function VariablesBody({
  allVariables,
  currentSort,
  setCurrentSort,
}) {
  (...)

  const [displayedVariables, setDisplayedVariables] = useState([]);

  const returningVariables = () => {
    setDisplayedVariables([]);
    let stockData = filteringVariables([...allVariables]);
    let stockVariables = [];
    if (currentSort === "default") {
      for (let i = 0; i < stockData.length; i++) {
        stockVariables.push(
          <SingleVariable
            variable={stockData[i]}
            key={`variable-${stockData[i].reference}-${i}`}
          />
        );
      }
    } else if (currentSort === "scope") {
      stockVariables.push(
        [...stockData]
          .sort((a, b) => {
            const cleanedA = a.scope.toLowerCase();
            const cleanedB = b.scope.toLowerCase();
            const diff = cleanedA.localeCompare(cleanedB);
            return diff;
          })
          .map((singleVariable) => {
            return (
              <SingleVariable
                variable={singleVariable}
                key={`variable-${singleVariable.reference}`}
              />
            );
          })
      );
    } else if (currentSort === "scope-reverse") {
      stockVariables.push(
        [...stockData]
          .sort((a, b) => {
            const cleanedA = a.scope.toLowerCase();
            const cleanedB = b.scope.toLowerCase();
            const diff = cleanedB.localeCompare(cleanedA);
            return diff;
          })
          .map((singleVariable) => {
            return (
              <SingleVariable
                variable={singleVariable}
                key={`variable-${singleVariable.reference}`}
              />
            );
          })
      );
    } else if (currentSort === "name") {
      stockVariables.push(
        [...stockData]
          .sort((a, b) => {
            const cleanedA = a.name.toLowerCase();
            const cleanedB = b.name.toLowerCase();
            const diff = cleanedA.localeCompare(cleanedB);
            return diff;
          })
          .map((singleVariable) => {
            return (
              <SingleVariable
                variable={singleVariable}
                key={`variable-${singleVariable.reference}`}
              />
            );
          })
      );
    } else if (currentSort === "name-reverse") {
      stockVariables.push(
        [...stockData]
          .sort((a, b) => {
            const cleanedA = a.name.toLowerCase();
            const cleanedB = b.name.toLowerCase();
            const diff = cleanedB.localeCompare(cleanedA);
            return diff;
          })
          .map((singleVariable) => {
            return (
              <SingleVariable
                variable={singleVariable}
                key={`variable-${singleVariable.reference}`}
              />
            );
          })
      );
    }
    setDisplayedVariables([...stockVariables]);
  };

  (...)

  return (
    <section>
      {displayedVariables.length > 0 ? displayedVariables : <MddLoader />}
    </section>
  );
}

And finally, SingleVariable :

export default function SingleVariable({ variable }) {
  const [variableName, setVariableName] = useState(
    variable ? variable.name : ""
  );
  const [variableValue, setVariableValue] = useState(
    variable ? variable.value : ""
  );
  const [variableFormat, setVariableFormat] = useState(
    variable ? variable.format : ""
  );
  const [variableScope, setVariableScope] = useState(
    variable ? variable.scope : ""
  );
  const [variableDescription, setVariableDescription] = useState(
    variable ? variable.description : ""
  );

  return (
    <article
      className={`variableContainer row${
        !variable.used ? " inactiveVariable" : ""
      }`}
    >
      <input
        className="col"
        list="scopeOptions"
        value={variableScope}
        onChange={(e) => {
          setVariableScope(e.target.value);
        }}
        disabled={!variable.used}
      />
      <datalist id="scopeOptions">
        <option value="app" />
        <option value="team" />
        <option value="business" />
      </datalist>
      <input
        className="col"
        value={variableName}
        onChange={(e) => {
          setVariableName(e.target.value);
        }}
        disabled={!variable.used}
      />
      <input
        className="col"
        value={variableValue}
        onChange={(e) => {
          setVariableValue(e.target.value);
        }}
        disabled={!variable.used}
      />
      <input
        className="col"
        value={variableFormat}
        onChange={(e) => {
          setVariableFormat(e.target.value);
        }}
        disabled={!variable.used}
      />
      <input
        className="col"
        value={variableDescription}
        onChange={(e) => {
          setVariableDescription(e.target.value);
        }}
        disabled={!variable.used}
        title={variableDescription}
      />
      {variable.used ? (
        <div className="variablePlaceholder" />
      ) : (
        <MyButton
          iconName="bi bi-x"
          buttonSize="small"
          smallButtonDescription="Delete variable"
          iconColor="altButtonColor"
        />
      )}
    </article>
  );
}

Thanks!

Leave a Comment