Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TipTap React: Update Editor Initial Content based on dropdown selection

I'm using TipTap as the rich text editor for my react project for updating the description of the product which has been selected from the dropdown.

The expected outcome is that the editor should show the initial content which has been saved in the database for the respective product.

However, using the 'content' option in editor allows me to set the initial content, it does not updates the initial content if i change the option in dropdown.

update data

My Editor Code:

import { useEditor, EditorContent } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import Underline from '@tiptap/extension-underline';
import Placeholder from '@tiptap/extension-placeholder';
import TipTapMenubar from './TipTapMenubar';

const TiptapEditor = ({ description, setDescription }) => {
  // console.log('description', description);

  const sampleDesc = `Describe your product in 4-5 bullet points...
    Point #1: this is an explanation of my product.
    Point #2: ....`;

  const editor = useEditor({
    extensions: [
      StarterKit,
      Underline,
      Placeholder.configure({
        placeholder: sampleDesc,
      }),
    ],
    content: `${description}`,
    onUpdate: ({ editor }) => {
      const html = editor.getHTML();
      setDescription(html);
    },
  });

  return (
    <div className='!mt-2 border border-gray-300 shadow rounded'>
      <TipTapMenubar editor={editor} />
      <EditorContent editor={editor} />
    </div>
  );
};

export default TiptapEditor;

This Editor component is then called in a parent component to display the data:

import { useState, useEffect } from 'react';
import Skeleton from 'react-loading-skeleton';
import TiptapEditor from '@/components/common/editor/TiptapEditor';
import InputInlineLabel from '@/components/form/InputInlineLabel';
import LoadingButton from '@/components/form/LoadingButton';

function ProductOptionNameDesc({
  product,
  selectedOption,
  name,
  setName,
  description,
  setDescription,
  loading,
  handleProductOptionData,
  handleProductOptionsDescription,
}) {
  const [enableEditor, setEnableEditor] = useState(false);
  // console.log('product', product);
  // console.log('selectedOption', selectedOption);
  // if selectedOption, set name and description from it
  useEffect(() => {
    if (selectedOption) {
      const productOptions = product?.product_options;
      productOptions?.map((option) => {
        if (option?.id === selectedOption?.id) {
          setName(option?.name);
          setDescription(option?.description);
        }
      });
      setEnableEditor(true);
    }
  }, [selectedOption]);

  // console.log('description', description);

  return (
    <div>
      <form onSubmit={handleProductOptionData}>
        <div className='space-y-4'>
          <div className='flex items-center gap-2'>
            <label htmlFor='poname' className='text-sm text-gray-600'>
              Product option name
            </label>
            <input
              id='poname'
              name='poname'
              placeholder='product name'
              className='w-full rounded p-2 border border-gray-300 text-sm placeholder:text-sm focus:outline-none focus:border-purple-500'
              value={name || ''}
              onChange={(e) => setName(e.target.value)}
            />
          </div>
          <div className='space-y-2'>
            {enableEditor ? (
              <TiptapEditor
                description={description}
                setDescription={setDescription}
                placeholder='Add up to 5 points describing the product...'
              />
            ) : (
              <div>
                <Skeleton height={20} />
                <Skeleton height={70} />
              </div>
            )}
          </div>

            {loading ? (
              <LoadingButton />
            ) : (
              <button className='text-sm w-full py-1 px-2 rounded cursor-pointer border border-violet-700 bg-violet-50 hover:bg-violet-100 text-violet-700'>
                Update name and description
              </button>
            )}
        </div>
      </form>
    </div>
  );
}

export default ProductOptionNameDesc;

What I am trying to achieve:

If a user selects the color brown in dropdown and there is already some initial value saved for the same in database for brown, then the useEffect hook updates the description state, however, it is not reflected in the tiptap editor content.

like image 677
ajhavery Avatar asked Jun 10 '26 17:06

ajhavery


2 Answers

Check if this helps: add description variable to dependency array of useEditor

 const editor = useEditor({
    ...
 },[description]);
like image 170
Sudarshan Shenoy Avatar answered Jun 13 '26 08:06

Sudarshan Shenoy


You'll probably want to run the effect inside of the Editor component to use the setContent function referenced in a comment above.

This would look something like

  const editor = useEditor({
    ...
  });

  useEffect(() => {
    editor.commands.setContent(description);
  }, [description]);

Re-rendering the editor by adding description as a dependency may work but is 1) more expensive, 2) may not appear precisely as you'd like and 3) doesn't trigger onTransaction or any of TipTap's callbacks or state changes that you may depend on in the future.

like image 43
jackfischer Avatar answered Jun 13 '26 08:06

jackfischer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!