Async Decorator Callback?
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
I can confirm this behaviour. The callback does not work in async. @Iliyass Did you solve this issue in the meantime?
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 :)