Home > Software design >  React State not updating properly
React State not updating properly

Time:03-10

I'm building a text editor using React with Typescript. The component hierarchy looks like this: TextEditor -> Blocks -> Block -> ContentEditable.

The ContentEditable is an npm package https://www.npmjs.com/package/react-contenteditable.

What i want it to do

The behavior I'm after is similar to Medium or Notions text editor. When a user writes in a block and hits enter on their keyboard, a new block should be created after the current block.

What it does

The behavior right now is strange to me. If I press enter and add one block, it works fine. But if I press enter again it overrides the previous block instead of creating a new one. However, if I press enter and add a block, then puts the carrot (focusing) on the new block and press enter again, a new block is added after as expected.

Sandbox

Here is a sandbox with the complete code: https://codesandbox.io/s/texteditor-mxgbey?file=/src/components/Block.tsx:81-557

TextEditor

export default function TextEditor(props) {
  const [blocks, setBlocks] = useState([
    { id: "1", tag: "h1", html: "Title1" },
    { id: "2", tag: "p", html: "Some text" }
  ]);

  function handleAddBlock(id: string) {
    const index = blocks.findIndex((b) => b.id === id);
    let copiedBlocks = [...blocks];
    let newBlock = { id: nanoid(), tag: "p", html: "New block..." };
    copiedBlocks.splice(index   1, 0, newBlock);
    setBlocks(copiedBlocks);
  }

  return <Blocks injectedBlocks={blocks} handleAddBlock={handleAddBlock} />;
}

Blocks

export default function Blocks(props) {
  const { injectedBlocks, handleAddBlock } = props;
  return (
    <>
      {injectedBlocks.map((b) => {
        return (
          <Block
            key={b.id}
            id={b.id}
            tag={b.tag}
            html={b.html}
            handleAddBlock={handleAddBlock}
          />
        );
      })}
    </>
  );
}

Block

export default function Block(props) {
  const { id, tag, html, handleAddBlock } = props;

  function handleChange(e: React.SyntheticEvent) {}
  function handleKeyDown(e: React.KeyboardEvent) {
    if (e.key === "Enter") {
      console.log("Enter pressed on: ", id);
      e.preventDefault();
      handleAddBlock(id);
    }
  }

  return (
    <ContentEditable
      tagName={tag}
      html={html}
      onChange={handleChange}
      onKeyDown={handleKeyDown}
    />
  );
}

CodePudding user response:

State value not give the updated value while handleAddBlock function calls. So use like this,

 setBlocks((p) => {
      let copiedBlocks = [...p];
      let newBlock = { id: nanoid(), tag: "p", html: "New block..." };
      copiedBlocks.splice(index   1, 0, newBlock);
      return copiedBlocks;
    });

This will gives the updated state value immediately.

  • Related