【问题标题】:Callback is not working in ajax request?回调在ajax请求中不起作用?
【发布时间】:2017-09-11 16:49:04
【问题描述】:

我正在尝试使用草稿 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。请发布有关strategy:linkStrategy的更多详细信息
  • 先生,从 LinkStrategy 我将回调传递给其他函数我发现从那里匹配的 url 回调将被执行 @MaximShoustin
  • @Nane - 是你的 passit 变量填充了passit = response.data。你能检查一下console.log吗?
  • 我的主要目标是将 url 数据传递给我的组件,是的 passit 有一些有效的 url 数据我已经测试过了..@nash_ag
  • @MaximShoustin 使用草稿 js 嵌入 url 的任何建议或任何替代方法

标签: javascript ajax reactjs axios draftjs


【解决方案1】:

下面描述的技术将帮助您实现预期的行为。

为什么您的解决方案不起作用: 需要由 callback 执行的所需操作未执行的原因是,draft 期望 callback 被同步调用.由于您使用的是async 函数(axios api 调用)并且异步调用callback 没有效果。

解决方案:这可能不是一个有效的解决方案,但可以完成工作。简而言之,您所要做的就是将axios 调用的结果(临时)存储在一个变量中,然后为您的editor 触发re-render,提前检索结果存储并使用它来调用回调。

我根据这个例子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 更新编辑器,这将强制 re-render。确保此函数绑定到您的组件,以便我们获得正确的 this(context)。

forceRenderEditor = () => {
  this.editor.update(this.state.editorState);
}

在您的 findLinkInText 函数中执行以下操作。

首先确保它(findLinkInText)绑定到您的组件,以便我们得到正确的this。您可以使用箭头函数来执行此操作或将其绑定到组件构造函数中。

其次,检查url 的结果是否已经在tempResults 中 我们在组件的构造函数中声明的。如果有,则立即使用适当的参数调用回调。

如果我们还没有结果,则进行调用并将结果存储在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 调用的响应,或者您可以从中确定需要传递给 callback 的参数。
  5. 我认为,如果每次渲染调用多个 api,您可能需要 debounce forceRenderEditor 方法以避免重复更新。
  6. 最后,我找不到draft 使用或建议async 回调的地方。如果他们支持/需要这样的功能,您可能需要咨询图书馆的团队。 (如果需要,进行更改并提出 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
  • 您需要将findLinkInText与组件绑定。或者你可以使用箭头功能来做到这一点。发生错误的原因是,findLinkInText 与您的组件不在同一上下文中。
  • 对不起我不擅长这个我已经尝试了很多你能不能用我的问题展示这个例子..@Panther
  • 它传递回调但期望函数是同步的有点蹩脚,这非常具有误导性。为什么不返回结果?我猜太简单了。
【解决方案2】:

假设一切正常,我会期待下面的更多内容。我没有准备好使用 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?
  • 查看帖子我已经更新了代码,但您的解决方案仍然无法正常工作...
【解决方案3】:

请注意,“axios 依赖于要支持的原生 ES6 Promise 实现。如果您的环境不支持 ES6 Promise,您可以使用 polyfill。”这意味着这不适用于所有浏览器。 (即使在此处包含推荐的包含https://github.com/stefanpenner/es6-promise

,我也无法让它在 IE 11 中工作

这是我在 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)
                })

            }
        }

【讨论】:

  • 再一次没有运气先生,我找不到任何线索!
  • 说明运气不好。它是否使任何调试器行变热?
  • handleWithAxiosCallBack 之后什么都没有发生
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-07
  • 1970-01-01
相关资源
最近更新 更多