draft-js icon indicating copy to clipboard operation
draft-js copied to clipboard

Async Decorator Callback?

Open Iliyass opened this issue 5 years ago • 2 comments

Do you want to request a feature or report a bug? feature request

What is the current behavior? Callback in the decorator doesn't work in async

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. You can use this jsfiddle to get started: https://jsfiddle.net/gmertk/e61z7nfa/.

What is the expected behavior? Callback can be executed asynchronously

Which versions of Draft.js, and which browser / OS are affected by this issue? Did this work in previous versions of Draft.js? All versions

Iliyass avatar Apr 09 '20 16:04 Iliyass

I can confirm this behaviour. The callback does not work in async. @Iliyass Did you solve this issue in the meantime?

digitalgopnik avatar Jan 19 '22 14:01 digitalgopnik

Clear now for me, why async doesn't work in decorator callback. I think i have a solution for this "problem" / a workaround for this issue.

So assuming that we want the decorator callback to work in async mode, there is something, that we need to wait for to be finished before we trigger the callback. Something like:

function handleStrategy(contentBlock, callback, contentState) {
  findWithApi(contentBlock, callback);
}

async function findWithApi(contentBlock, callback) {
  const response = await fetch('http://myapi.com/data.json')
  response.data.forEach((entry) => {
    callback(entry.start, entry.end);
  }
}

As described in the previous posts, this won't work - the callback "dies". So a work-around for this would be to move the request out of the decorator-strategy and initialize the decorator again, after the request to the api completed successfully and pass the fetched data as "optionalProps" to the Decorator-component and -strategy:

const DecoratorComponent = (optionalProps) => {
  // you can use optionalProps here
  
  return (children) => {
    return <span style={{ backgroundColor: "orange"}}>{children.children}</span>
  }
}

const DraftEditor = () => {
  const [optionalProps, setOptionalProps] = useState([]);
  const decoratorStrategy = (currentOptionalProps) => {
    // you can use currentOptionalProps here
    
    return (contentBlock, callback, contentState) => {
      currentOptionalProps.forEach((currentOptionalProp) => {
        callback(currentOptionalProp.start, currentOptionalProp.end);
      })
    }
  }

  const createDecorator = (currentOptionalProps) => {
    return new CompositeDecorator([
    { 
      strategy: decoratorStrategy(currentOptionalProps), 
      component: DecoratorComponent(currentOptionalProps),
    }
  ]);
    
  const compositeDecorator = createDecorator(optionalProps);

  const [editorState, setEditorState] = useState(EditorState.createEmpty(createDecorator));
  const handleChange = (changedEditorState) => {
    setEditorState(changedEditorState);
    fetch('http://myapi.com/data.json').then(response => response.json()).then(data => reinitializeDecorator(data));
  }
  
  const reinitializeDecorator = (data) => {
    setOptionalProps(data);
    // the decorator will be defined with the fetched data from the api
    const updateDecorator = createDecorator(data);

    // the editorState will be updated with defined decorator (with fetched data)
    const updateEditorState = EditorState.set(editorState, { decorator: updateDecorator });
    // the decorator will be triggered again, we are now able to wait for any async task 
    // (no matter whether its an api-call, we have to wait for or any other action)
    setEditorState(updateEditorState);
  }


  return (
    <div>
      <Editor placeholder={'type: decorator can be used in async now'} editorState={editorState} onChange={handleChange} />
    </div>
  )
}

So the solution would be, that we simply "re-initialize" the decorator and update the editorState after our asynchronous-task completed.

Hope this helps anyone out there. Feel free to ask for help, if any questions :)

digitalgopnik avatar Feb 04 '22 08:02 digitalgopnik