skip to Main Content

update

From some some suggestions I received on this question, I’ve now found that either setting my recipe.ingredients to local storage or using setContent with useEffect as shown below does set a persistent value in the tiptap editor. However, using either of those approaches results in a different problem, which is that the content does not render until I refresh the page. That means that the editor is empty unless the page is refreshed due to some kind of delay. Just like the problem described below, if I type in an html value, I don’t have this problem. For example, if I console.log(recipe.ingredients) and then cut and paste the html from the console into content on the editor, everything works fine, but if I try to pass recipe.ingredients, it won’t render until I refresh the page. Since it works when I type in an html string wrapped in quotes, I have tried wrapping the value in quotes and then passing it like this:

`'`+ recipe.ingredients + `'` 

that only results in the same problem described in my initial question but with quotes showing.

To sum up, this won’t work until page refresh:

const editor = useEditor({

    extensions,
        content:'',
        onUpdate: ({ editor }) => {
      const html = editor.getHTML();
                setIngredients(html);
        editor.commands.clearContent()
      console.log(html);
    },
  })

  useEffect(() => {
    if (!editor) {
      return undefined
    }

    editor.commands.setContent(recipe.ingredients)
  }, [editor])

  if (!editor) {
    return null
  } 

but the html value of recipe.ingredients written out like this works fine:


const editor = useEditor({

    extensions,
        content:'',
        onUpdate: ({ editor }) => {
          const html = editor.getHTML();
          setIngredients(html);
      console.log(html);
    },
  })

  useEffect(() => {
    if (!editor) {
      return undefined
    }

    editor.commands.setContent('<ul><li><p>mac</p></li><li> 
      <p>cheese</p></li></ul>')
    }, [editor])

    if (!editor) {
    return null
  } 

end update
Original question:

I’m having a problem with loosing the content of my TipTap editor (based on ProseMirror) on refresh when I’m passing data from a field in my database into the content of the TipTap editor.

My goal is to take the ingredients field from the recipe object in my database and pass it to the tiptap editor. The data is in html format and was created using a TipTap editor in my "Add a Recipe" form. I want the data to appear and be editable in this new TipTap editor, which will be a part of a form for the user to "Edit a Recipe". Note that I believe this is the same question that was asked here: How to persist data so that it does not get erased in TipTap editor? However that question didn’t provide any sample code, so I’m providing some more context in my question.

Here is my current code where I am passing the recipe.ingredients data to the TipTap editor content:

// define your extension array
const extensions = [
  Color.configure({ types: [TextStyle.name, ListItem.name] }),
  TextStyle.configure({ types: [ListItem.name] }),
  StarterKit.configure({
    bulletList: {
      keepMarks: true,
      keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
    },
    orderedList: {
      keepMarks: true,
      keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
    }
  }),
  // Placeholder.configure({
  //   placeholder: 'Ingredients',
  // }),
  Underline, 
  TextAlign.configure({
    types: ['heading', 'paragraph'], 
    alignments: ['left', 'right'],
    defaultAlignment: 'left',
  })
]

const TiptapIngredientsEdit = ({setIngredients}) => {
    const { id }= useParams();

    const initialRecipeState = {
    id: null,
    title: "",
    description: "",
    recipeType: "",
    servingSize: null,
    ingredients: "",
    directions: "",
    source: "",
    userId: undefined
  };

  const [recipe, setRecipe] = useState(initialRecipeState);

     //get recipe
     const getRecipe = id => {
    RecipeDataService.get(id)
    .then(response => {
      setRecipe(response.data);
      console.log(response.data.ingredients);
    })
    .catch(e => {
      console.log(e);
    });
  };
  
  useEffect(() => {
    if(id)
    getRecipe(id);
  }, [id]);

  const editor = useEditor({
    extensions,
    content: recipe.ingredients,
    onUpdate: ({ editor }) => {
      const html = editor.getHTML();
      setIngredients(html);
      console.log(html);
    }
  })

  return (
  <>
    <Box sx={{ mb: "4px", border: 1, borderColor: 'rgb(196, 196, 196)', borderRadius: '5px'}}>
      <MenuBar editor={editor} />
      <EditorContent editor={editor} /> 
    </Box>
  </>
  )
}

export default TiptapIngredientsEdit

The ingredients render from the html as expected at first and can be edited as desired:

screenshot of results showing data in tiptap editor

But after a page refresh, the editor content goes blank:

screenshot of blank editor contents

I do not have the same issue if I provide an html string as the content like this, so it appears to be an issue with passing the data from the database to the content:

const editor = useEditor({

    extensions,
    content: '<b>test content</b>',
    onUpdate: ({ editor }) => {
      const html = editor.getHTML();
      setIngredients(html);
      console.log(html);
    }
  })

2

Answers


  1. Chosen as BEST ANSWER

    This was the final version that worked for me to solve all these problems, based on the answer from I_am_prince, but with a few changes to make it work correctly for my code:

        const editor = useEditor({
    
        extensions,
            content: '',
            onUpdate: ({ editor }) => {
          const html = editor.getHTML();
                    setIngredients(html);
          console.log(html);
        },
        contentEditable: "true"
      })
    
      useEffect(() => {
        if (editor && !editor.isDestroyed) editor.commands.setContent(recipe.ingredients);
      }, [recipe.ingredients]);
    
      return (
      <>
        <Box sx={{ mb: "4px", border: 1, borderColor: 'rgb(196, 196, 196)', borderRadius: '5px'}}>
          <MenuBar editor={editor} />
          <EditorContent editor={editor} /> 
         
        </Box>
      </>
      )
    }
    

  2. You need ingredients in to edit and your ingredients is blank when useEditor initialise, we need to set content to editor object directly.

    So as per solution just set ingredients to editor using setContent command. you can check detail here.
    https://tiptap.dev/api/commands/set-content

    Code Need to Add

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

    Your Updated Code

    // define your extension array
    const extensions = [
      Color.configure({ types: [TextStyle.name, ListItem.name] }),
      TextStyle.configure({ types: [ListItem.name] }),
      StarterKit.configure({
        bulletList: {
          keepMarks: true,
          keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
        },
        orderedList: {
          keepMarks: true,
          keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
        }
      }),
      // Placeholder.configure({
      //   placeholder: 'Ingredients',
      // }),
      Underline, 
      TextAlign.configure({
        types: ['heading', 'paragraph'], 
        alignments: ['left', 'right'],
        defaultAlignment: 'left',
      })
    ]
    
    const TiptapIngredientsEdit = ({setIngredients}) => {
        const { id }= useParams();
    
        const initialRecipeState = {
        id: null,
        title: "",
        description: "",
        recipeType: "",
        servingSize: null,
        ingredients: "",
        directions: "",
        source: "",
        userId: undefined
      };
    
      const [recipe, setRecipe] = useState(initialRecipeState);
    
         //get recipe
         const getRecipe = id => {
        RecipeDataService.get(id)
        .then(response => {
          setRecipe(response.data);
          console.log(response.data.ingredients);
        })
        .catch(e => {
          console.log(e);
        });
      };
      
      useEffect(() => {
        if(id)
        getRecipe(id);
      }, [id]);
    
      useEffect(() => {
         editor.commands.setContent(recipe.ingredients)
      }, [recipe]);
    
      const editor = useEditor({
        extensions,
        content: recipe.ingredients,
        onUpdate: ({ editor }) => {
          const html = editor.getHTML();
          setIngredients(html);
          console.log(html);
        }
      })
    
      return (
      <>
        <Box sx={{ mb: "4px", border: 1, borderColor: 'rgb(196, 196, 196)', borderRadius: '5px'}}>
          <MenuBar editor={editor} />
          <EditorContent editor={editor} /> 
        </Box>
      </>
      )
    }
    
    export default TiptapIngredientsEdit
    

    Thank you

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search