0

I bet has been answered a million times, but I haven't had luck searching in google. I don't know a concise way to phrase the question.

As learning project, I am trying create a simple html editing tool, that includes a side panel where you can edit different properties of each layout element, like in figma. The problem is that in said panel, the input values do not update when I select a different element.

enter image description here

The values in the target objects are changed well, according to plan, but the problem is that the input values persist when I change from one object to another.

I created a demonstration isolating the problem here https://codesandbox.io/p/sandbox/react-question-example-3qlyp5 (also below). Here I can select among the two objects, and correctly alter the 'x' field of said object by editing the input. However, when I select the other object, the input value is not updated to show the value of the new object's field. Only the first selection updates the field value correctly.

I am tempted to remove the input and immediately show it again, as to re-instance it, but that's sub-optimal. Since it's a learning project I better ask.

import "./styles.css";
import { useState } from "react";

const exampleThings = [
  {
    x: "12px",
    i: 0,
    a: "foo",
  },
  {
    x: "5px",
    i: 1,
    a: "bar",
  },
];

const positionParse = (pos) => {
  const parsed = parseFloat(pos);
  if (isNaN(parsed)) throw new Error("oops");
  return parsed;
};

function PositionEditor({ setPositionDef, positionDef }) {
  const [hasError, setHasError] = useState(false);
  const [value, setValue] = useState(positionDef);

  const changed = (e) => {
    setValue(e.target.value);
    try {
      positionDef = positionParse(e.target.value);
      setHasError(false);
      setPositionDef(e.target.value);
    } catch (e) {
      setHasError(true);
    }
  };

  return (
    <input
      type="text"
      value={value}
      onChange={changed}
      style={{ borderColor: hasError ? "red" : "unset" }}
    />
  );
}

function ThingSelector({ select, t }) {
  return <button onClick={select}>Select thing {t} </button>;
}

export default function App() {
  const [things, setThings] = useState([...exampleThings]);
  const [selectedThing, setSelectedThing] = useState(undefined);
  const selectThing = (i) => {
    const thingIndex = things.findIndex((thing) => thing.i === i);
    console.log("select thing", thingIndex);
    setSelectedThing(things[thingIndex]);
  };
  const editThingPositionDef = (position) => {
    const selectedThingId = things.findIndex((t) => t.i === selectedThing.i);
    const newThings = [...things];
    const newSelectedThing = {
      ...selectedThing,
      x: position,
    };

    newThings[selectedThingId] = {
      position,
      ...newSelectedThing,
    };
    setThings(newThings);
    setSelectedThing(newSelectedThing);
  };
  return (
    <div className="App">
      {things.map((thing) => {
        return (
          <ThingSelector
            select={(e) => selectThing(thing.i)}
            t={thing.a}
            key={thing.i}
          />
        );
      })}

      {selectedThing === undefined ? (
        <p>Select something</p>
      ) : (
        <>
          <p>
            Editing: {selectedThing.a}. My x is {positionParse(selectedThing.x)}
          </p>
          <PositionEditor
            setPositionDef={editThingPositionDef}
            positionDef={selectedThing.x}
          />
        </>
      )}
    </div>
  );
}
1
  • chatgpt also told me about the redundant 'position' object property assign on line 69.. oops! Commented Aug 9, 2024 at 0:17

1 Answer 1

0

ok! funnily enough, ChatGpt gave me a useful answer!

What I needed is a new useEffect hook - that's why I needed a real project to understand the use of the hooks.

Here the fixed code (the only change is the addition of the useEffect hook)

import "./styles.css";
import { useState, useEffect } from "react";

const exampleThings = [
  {
    x: "12px",
    i: 0,
    a: "foo",
  },
  {
    x: "5px",
    i: 1,
    a: "bar",
  },
];

const positionParse = (pos) => {
  const parsed = parseFloat(pos);
  if (isNaN(parsed)) throw new Error("oops");
  return parsed;
};

function PositionEditor({ setPositionDef, positionDef }) {
  const [hasError, setHasError] = useState(false);
  const [value, setValue] = useState(positionDef);

  useEffect(() => {
    setValue(positionDef);
  }, [positionDef]);

  const changed = (e) => {
    setValue(e.target.value);
    try {
      positionDef = positionParse(e.target.value);
      setHasError(false);
      setPositionDef(e.target.value);
    } catch (e) {
      setHasError(true);
    }
  };

  return (
    <input
      type="text"
      value={value}
      onChange={changed}
      style={{ borderColor: hasError ? "red" : "unset" }}
    />
  );
}

function ThingSelector({ select, t }) {
  return <button onClick={select}>Select thing {t} </button>;
}

export default function App() {
  const [things, setThings] = useState([...exampleThings]);
  const [selectedThing, setSelectedThing] = useState(undefined);
  const selectThing = (i) => {
    const thingIndex = things.findIndex((thing) => thing.i === i);
    console.log("select thing", thingIndex);
    setSelectedThing(things[thingIndex]);
  };
  const editThingPositionDef = (position) => {
    const selectedThingId = things.findIndex((t) => t.i === selectedThing.i);
    const newThings = [...things];
    const newSelectedThing = {
      ...selectedThing,
      x: position,
    };

    newThings[selectedThingId] = {
      position,
      ...newSelectedThing,
    };
    setThings(newThings);
    setSelectedThing(newSelectedThing);
  };
  return (
    <div className="App">
      {things.map((thing) => {
        return (
          <ThingSelector
            select={(e) => selectThing(thing.i)}
            t={thing.a}
            key={thing.i}
          />
        );
      })}

      {selectedThing === undefined ? (
        <p>Select something</p>
      ) : (
        <>
          <p>
            Editing: {selectedThing.a}. My x is {positionParse(selectedThing.x)}
          </p>
          <PositionEditor
            setPositionDef={editThingPositionDef}
            positionDef={selectedThing.x}
          />
        </>
      )}
    </div>
  );
}

Sign up to request clarification or add additional context in comments.

2 Comments

Dang, I was gonna give you a cryptic answer and let you figure it out lol
hah .. might've been more fun

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.