回调在ajax请求中不起作用?

5
我正在尝试使用Draft JS构建一个内容编辑器。具体功能是从像Facebook这样的URL中提取数据。但是我在这个部分卡住了,回调函数无法正常工作。
首先,我使用compositeDecorator将我的状态进行了包装,就像这样:
constructor(props) {
    super(props);
    const compositeDecorator = new CompositeDecorator([
        .... {
            strategy: linkStrategy,
            component: decorateComponentWithProps(linkComp, {
                passit
            })
        }
        ....
    ]);
}
// This is my strategy
function linkStrategy(contentBlock, callback, contentState) {
    findLinkInText(LINK_REGEX, contentBlock, callback)
}

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);
        axios.post('/url', {
            url: URL
        }).then(response => {
            passit = response.data
            //not working
            callback(start, start + URL.length)
        })
        //working
        callback(start, start + URL.length)
    }
}

如果回调函数不起作用,组件将无法渲染。 我不知道这是否是一个基本的JavaScript问题。但问题是,我想从我的服务器获取url数据,并且我必须通过props传递数据到我的组件并进行渲染。
答案更新:
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];
        axios.post('/url', {
            url: URL
        }).then(response => {
            passit = response.data
            handleWithAxiosCallBack(start, start + matchArr[0].length, callback)
        }).catch(err => console.log(err))
    }
}


function handleWithAxiosCallBack(start, startLength, callback) {
    console.log(callback); //Spits out the function But Not working
    callback(start, startLength)
}

先生,我从LinkStrategy传递回调到其他函数,我从那里找到匹配的URL,回调将被执行@MaximShoustin。 - Nane
我的主要目标是将URL数据传递给我的组件,而且passit有一些可用的URL数据,我已经测试过了。@nash_ag - Nane
由于它在“then”函数内部未定义,所以它无法正常工作。 - Bindrid
请给出一个例子来回答我的问题 @Bindrid - Nane
服务器是否收到并响应了/url请求? - Jiang YD
显示剩余7条评论
3个回答

1
以下描述的技巧将帮助您实现期望的行为。
为什么您的解决方案不起作用:需要执行的所需操作未能执行,因为“draft”希望同步调用“callback”。由于您正在使用异步函数(axios api调用)并且异步调用回调没有效果。
解决方案:这可能不是一种有效的解决方案,但可以完成工作。简单地说,您需要做的就是将来自“axios”调用的结果存储在一个变量中(暂时),然后触发您的编辑器的“重新呈现”,检索之前存储的结果并使用它来调用回调。
我正在基于此示例here进行跟进。假设您在组件状态中存储了编辑器状态。以下是伪代码,您可能需要根据自己的需求实现。
假设您的组件状态如下,其中包含 Editor 的状态。
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 = {}; 

}

假设您正在渲染类似以下内容的Editor。请注意ref。这里,编辑器的引用存储在组件的editor变量中,稍后可以访问它。使用字符串作为 ref 也可以起作用,但建议使用此方法存储 refs。
 <Editor
    ref={ (editor) => this.editor }
    editorState={this.state.editorState}
    onChange={this.onChange}
    // ...your props
 />

在你的组件中,编写一个函数以使用currentState更新编辑器,从而强制重新渲染。确保将此函数绑定到您的组件,以便我们获取正确的this(上下文)。
forceRenderEditor = () => {
  this.editor.update(this.state.editorState);
}

在您的findLinkInText函数中,请执行以下操作。
首先确保它(findLinkInText)绑定到您的组件,以便我们获得正确的this。您可以使用箭头函数来实现这一点,或者在组件构造函数中进行绑定。
其次,检查我们是否已经在组件构造函数中声明的tempResults中具有url的结果。如果我们有一个结果,则立即使用适当的参数调用回调函数。
如果我们没有结果,则进行调用并将结果存储在tempResults中。存储后,调用已定义的this.forceRenderEditor方法,这将调用draft重新检查,由于我们已经将结果存储在tempResults中,因此将调用回调函数,并反映适当的更改。
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);

     // do we have the result already,?? 
     // call the callback based on the result.
     if(this.tempResults[url]) {
         // make the computations and call the callback() with necessary args
     } else {
     // if we don't have a result, call the api
      axios.post('/url', {
         url: URL
      }).then(response => {
         this.tempResults[url] = response.data;
         this.forceRenderEditor();
         // store the result in this.tempResults and 
         // then call the this.forceRenderEditor
         // You might need to debounce the forceRenderEditor function
      })
    }
 }
}

注意:

  1. 您需要确定是否需要清除tempResults。如果需要,您需要在适当的位置实现相应的逻辑。
  2. 要存储tempResults,您可以使用一种称为“memoization”的技术。上述描述的是一种简单的方法。
  3. 由于您的结果被记忆化了,如果axios api调用的结果对于相同的输入不会改变,这可能对您有利。您可能不必为相同的查询再次调用api。
  4. 您在tempResults中存储的数据应该是来自api调用的响应或者您可以确定需要传递给回调函数的参数的东西。
  5. 我认为,您可能需要将forceRenderEditor方法进行防抖处理,以避免每次渲染时都调用许多api而导致重复更新。
  6. 最后,我找不到draft在哪里使用或建议使用异步回调的地方。如果他们支持/需要此功能,您可能需要与库的团队进行确认。(如果需要,请进行更改并在他们的团队同意后提出PR。)

更新

为了绑定,您可以将函数移动到组件内并按以下方式编写。
linkStrategy = (contentBlock, callback, contentState) => {
    this.findLinkInText(LINK_REGEX, contentBlock, callback)
}


findLinkInText = (...args) => {
}

在你的构造函数中,你可以这样调用它。
const compositeDecorator = new CompositeDecorator([
    .... {
        strategy: this.linkStrategy,
        component: decorateComponentWithProps(linkComp, {
            passit
        })
    }
    ....
 ]);
}

如果您想在多个组件中重复使用该函数,则可以按以下方式绑定它。但是,请确保在所有共享组件中使用相同的state(或使用回调函数定义自定义状态)

您的构造函数将会像这样

const compositeDecorator = new CompositeDecorator([
    .... {
        strategy: linkStrategy.bind(this),
        component: decorateComponentWithProps(linkComp, {
            passit
        })
    }
    ....
 ]);
}

你的链接策略将像这样。
 function linkStrategy(contentBlock, callback, contentState) {
    findLinkInText.call(this,LINK_REGEX, contentBlock, callback);
 }

您可以使用上述任一方法来绑定您的函数。

你的解决方案有点可行,但是它会抛出一个错误,就像这样 TypeError: _this4.forceRenderEditor is not a function - Nane
你需要将 findLinkInText 与组件绑定,或者使用箭头函数来实现。错误的原因是 findLinkInText 不在与你的组件相同的上下文中。 - Panther
抱歉,我不太擅长这个,我已经尝试了很多次。能否请您用我的问题给出一个示例?@Panther - Nane
有点遗憾它传递了一个回调函数,但却期望该函数是同步的,这非常具有误导性。为什么不直接返回结果呢?可能太简单了吧。 - Preview

-1

假设其他一切正常,我期望得到更多下面的内容。但我没有设置使用Axios,所以无法实际测试。

// I made these global variables because you are trying to use them
// in both the .post and the .then
var start; // global variable
var URL // global variable
function processResponse (aStart, aStartURL, passit) {
    // do something with the reponse data
}

function findLinkInText(regex, contentBlock) {
    const text = contentBlock.getText();
    let matchArr;
    if((matchArr = regex.exec(text)) !== null){
        start = matchArr.index;
        // renamed because it is not the url, its data passed to the server
        URL = matchArr[0];
        console.log(URL);
        axios.post('/url',{url:URL}).then(response => {
            passit = response.data;
            processResponse (start, start + URL.length, passit)
        }).catch(function (error) {
            console.log(error);
        });
    }
}

被调用的是then吗?被调用的是catch吗? - Bindrid
看到你的回复了,我已经更新了代码并加入了你提供的解决方案,但还是无法正常工作... - Nane

-1
请注意:"axios 依赖原生的 ES6 Promise 实现来支持。如果您的环境不支持 ES6 Promises,您可以使用 polyfill。" 这意味着它并不能在所有浏览器中工作。(即使按照此处推荐的包含方法,我也无法在 IE 11 中使其工作https://github.com/stefanpenner/es6-promise
以下是我在 Edge 中成功运行的代码:
            axios(
                {
                    method: 'post',
                    url: '/wsService.asmx/GetDTDataSerializedList',
                    data: { parameters: 'one' },
                    callback: function () { alert() }
                })
                  .then(response =>{
                      response.config.callback();

                      console.log(response);
                  })
                  .catch(function (error) {
                      console.log(error);
                  });
             });

我已经相应地更改了您的代码,如下所示

        function findLinkInText(regex, contentBlock, callback) {
            const text = contentBlock.getText();
            let matchArr, start;
            if ((matchArr = regex.exec(text)) !== null) {
                start = matchArr.index;
                var URL = matchArr[0];
                console.log(URL);

                // I am putting the parameters used to make the call in a JSON object to be used in the "then"
                var myparameters = { myCallback: callback, URL: URL, start: start };

                axios({
                    method:post, 
                    url:'/url',
                    data: { url: URL },
                    // note that i am attaching the callback to be carrired through
                    myParameters: myparameters
                }).then(response => {
                    // This section is the callback generated by the post request. 
                    // it cannot see anything unless they declared globally or attached to the request and passed through
                    // which is what i did.
                    passit = response.data
                    var s = response.config.myParameters.start;
                    var u = response.config.myParameters.URL
                    response.config.myParameters.myCallback(s, s + u.length)
                })

            }
        }

先生,很抱歉,我又没有找到任何线索! - Nane
解释一下,没有运气。它有没有触发任何调试器行? - Bindrid
处理完Axios回调后,什么也没有发生。 - Nane

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接