The following method will help you achieve your expected behavior.
Why your solution doesnβt work: The reason why the required action that should be performed using callback is not executed is because draft expects the callback be called synchronous, since you are using the async function (calling axios api) and calling asynchronously callback , it does not affect.
Solution: This may not be an effective solution, but it can be implemented. Simply put, all you have to do is save the results of the axios call in a variable (temporarily) and then run re-render for your editor , first get the results store and use it to call the callback.
I follow this example here . Assuming you keep the editor state in component state. Below is the pseudo code that you may need to implement according to your needs.
Suppose your component state seems lower than the editor state.
constructor(props){ super(props); // .. your composite decorators // this is your component state holding editors state this.state = { editorState: EditorState.createWithContent(..) } // use this to temporarily store the results from axios. this.tempResults = {}; }
Assuming you pass your editor to something like below. Pay attention to ref . Here the link of the editors is stored in the editor variable of the component, which you can access later. Using a string like ref will work, but this is the recommended way to store links.
<Editor ref={ (editor) => this.editor } editorState={this.state.editorState} onChange={this.onChange}
In your component, write a function to update the editor using currentState, which will force re-render . Make sure that this function is bound to your component so that we get the correct this (context).
forceRenderEditor = () => { this.editor.update(this.state.editorState); }
In your findLinkInText function findLinkInText do the following.
First make sure that it (findLinkInText) is bound to your component, so we get the correct this . You can use the arrow function to do this or link it in the component constructor.
Secondly, check if the result for url already in tempResults which we declared in the component constructor. If we have one, then call the callback immediately with the appropriate arguments.
If we donβt already have a result, make a call and save the result in tempResults . After saving, call the already defined method this.forceRenderEditor , which will call a draft for re-checking, and this time, since we saved the results in tempResults , a tempResults will be called and the corresponding changes will be reflected.
function findLinkInText(regex, contentBlock, callback) { const text = contentBlock.getText(); let matchArr, start; if ((matchArr = regex.exec(text)) !== null) { start = matchArr.index; let URL = matchArr[0]; console.log(URL);
Note:
- You need to determine whether to clear tempResults. If so, you need to implement the logic for it in the appropriate place.
- To save tempResults, you can use a technique called
memoization . The above is one simple. - Since your results are noticed, this may be an advantage for you if the results of invoking the
axios api do not change for the same input. You may not have to hit the api again for the same request. - The data stored in your tempResults should be the response from an api call or something from which you can determine the arguments you need to pass to the
callback . - It seems to me that you might need the
debounce forceRenderEditor method to avoid re-updating if a lot of api is called for each rendering. - Finally, I could not find a place where
draft uses or suggests async call back. You may need to consult the library team if they support / need such a function. (If necessary, make changes and raise the PR, if their team is in order with one.)
Update
To bind, you can move functions inside the component and write it below.
linkStrategy = (contentBlock, callback, contentState) => { this.findLinkInText(LINK_REGEX, contentBlock, callback) } findLinkInText = (...args) => { }
And in your constructor you can call it like this
const compositeDecorator = new CompositeDecorator([ .... { strategy: this.linkStrategy, component: decorateComponentWithProps(linkComp, { passit }) } .... ]); }
Or, if you want to reuse a function for multiple components, you can bind it below. But remember to use the same state in all sharing components (or use a callback to define user states)
Your constructor will look like
const compositeDecorator = new CompositeDecorator([ .... { strategy: linkStrategy.bind(this), component: decorateComponentWithProps(linkComp, { passit }) } .... ]); }
Your link strategy will be like this:
function linkStrategy(contentBlock, callback, contentState) { findLinkInText.call(this,LINK_REGEX, contentBlock, callback); }
You can use any of the above methods to bind your functions.