gpt4 book ai didi

ruby-on-rails - 在 Rails 5.1 中使用 Turbolinks 无法正确呈现 React 组件

转载 作者:行者123 更新时间:2023-12-03 13:42:18 26 4
gpt4 key购买 nike

我有一个非常简单的 Rails 应用程序,它有一个 React 组件,它只在特定页面(假设显示页面)的现有 div 元素中显示“Hello”。

当我使用其 URL 加载相关页面时,它可以工作。我在页面上看到你好。

但是,当我之前在另一个页面上时(假设是索引页面,然后我使用 Turbolinks 转到显示页面,好吧,除非我再次来回切换,否则不会呈现该组件。(回到索引页面)并返回显示页面)

从这里每次来回,我可以说 View 渲染了两次。不仅是两次,而且是两次! (即 2 次,然后是 4 次,然后是 6 次等等。)

我知道,因为同时我设置了 div 的内容我向控制台输出一条消息。

事实上,我想回到索引页应该仍然运行没有显示的组件代码,因为 div 元素不在索引页上。但为什么以累积的方式呢?

我想解决的问题是:

  • 在显示页面的第一个请求上运行代码
  • 阻止代码在其他页面(包括索引页面)中运行
  • 让代码在显示页面的后续请求上运行一次


  • 这是我使用的确切步骤和代码(我会尽量简洁。)
  • 我有一个安装了 react 的 Rails 5.1 应用程序:
    rails new myapp --webpack=react
  • 然后我创建了一个简单的 Item 脚手架来获取一些页面:
    rails generate scaffold Item name
  • 我只是在 Show 页面( app/views/items/show.html.erb )中添加以下 div 元素:
    <div id=hello></div>
  • Webpacker 已经生成了一个 Hello 组件( hello_react.jsx ),我对其进行了如下修改以使用上述 div 元素。我改了原来的'DOMContentLoaded'事件:
    document.addEventListener('turbolinks:load', () => {
    console.log("DOM loaded..");
    var element = document.getElementById("hello");
    if(element) {
    ReactDOM.render(<Hello name="React" />, element)
    }
    })
  • 然后我在上一个 View ( app/views/items/show.html.erb )的底部添加了以下 webpack 脚本标签:
    <%= javascript_pack_tag("hello_react") %>
  • 然后我运行 rails serverwebpack-dev-server使用 foreman start (通过在 gem 'foreman' 中添加 Gemfile 来安装)。这是Procfile的内容我用了:
    web:     bin/rails server -b 0.0.0.0 -p 3000
    webpack: bin/webpack-dev-server --port 8080 --hot


  • 以下是重现所描述行为的步骤:
  • 使用 URL http://localhost:3000/items 加载索引页面
  • 单击“新建项目”以添加新项目。 Rails 重定向到 URL localhost:3000/items/1 处的项目显示页面。 .在这里我们可以看到 Hello React!信息。它运作良好!
  • 使用 URL http://localhost:3000/items 重新加载索引页面.该项目按预期显示。
  • 使用 URL http://localhost:3000/items/1 重新加载节目页面. Hello 消息按预期与一条控制台消息一起显示。
  • 使用 URL http://localhost:3000/items 重新加载索引页面
  • 单击显示链接(应通过 turbolink 执行)。留言是 不是 没有显示控制台消息。
  • 点击 Back 链接(应该通过 turbolink 执行)转到索引页面。
  • 再次单击 Show 链接(应通过 turbolink 执行)。这次消息显示得很好。控制台消息显示为 两次 .

  • 从那里每次我回到索引并再次回到显示页面时,每次都会在控制台上显示另外两条消息。

    注意:如果我让原始 hello_react,而不是使用(和替换)特定的 div 元素在附加 div 元素的文件中,这种行为更加明显。
    编辑:另外,如果我更改 link_to包含 data: {turbolinks: false} 的链接.它运作良好。就像我们使用浏览器地址栏中的 URL 加载页面一样。

    我不知道我做错了什么..
    有任何想法吗?

    编辑:如果有兴趣尝试此操作,我将代码放在以下存储库中:
    https://github.com/sanjibukai/react-turbolinks-test

    最佳答案

    这是一个相当复杂的问题,恐怕我不认为它有一个直截了当的答案。我会尽我所能解释!

    • To get the code run on the first request of the show page


    您的 turbolinks:load事件处理程序没有运行,因为您的代码在 turbolinks:load 之后运行事件被触发。这是流程:
  • 用户导航到显示页面
  • turbolinks:load触发
  • 评估正文中的脚本

  • 所以 turbolinks:load在下一个页面加载之前,不会调用事件处理程序(因此不会呈现您的 React 组件)。

    要(部分)解决此问题,您可以删除 turbolinks:load事件监听器,并调用 render直接地:
    ReactDOM.render(
    <Hello name="React" />,
    document.body.appendChild(document.createElement('div'))
    )

    或者,您可以使用 <%= content_for … %>/ <%= yield %>head 中插入脚本标签.例如在您的 application.html.erb 布局中

    <head>

    <%= yield :javascript_pack %>

    </head>


    然后在你的 show.html.erb 中:
    <%= content_for :javascript_pack, javascript_pack_tag('hello_react') %>

    在这两种情况下,对于您在 turbolinks:load 中使用 JavaScript 添加到页面的任何 HTML 都是毫无值(value)的。阻止,您应该在 turbolinks:before-cache 上将其删除以防止重新访问页面时出现重复问题。在您的情况下,您可能会执行以下操作:
    var div = document.createElement('div')

    ReactDOM.render(
    <Hello name="React" />,
    document.body.appendChild(div)
    )

    document.addEventListener('turbolinks:before-cache', function () {
    ReactDOM.unmountComponentAtNode(div)
    })

    即使有这一切,您在重新访问页面时仍可能会遇到重复问题。我相信这与渲染预览的方式有关,但我无法在不禁用预览的情况下修复它。

    • To get the code run once on subsequent requests of the show page
    • To block the code from running in other pages (including the index page)


    正如我上面提到的,在使用 Turbolinks 时,动态包含特定于页面的脚本可能会造成困难。 Turbolinks 应用程序中的事件监听器的行为与没有 Turbolinks 的事件监听器的行为非常不同,其中每个页面都会获得一个新的 document因此事件监听器会自动删除。除非您手动删除事件监听器(例如在 turbolinks:before-cache 上),否则对该页面的每次访问都会添加另一个监听器。更重要的是,如果 Turbolinks 缓存了该页面,则 turbolinks:load事件将触发两次:一次用于缓存版本,另一次用于新副本。这可能就是您看到它被渲染 2、4、6 次的原因。

    考虑到这一点,我最好的建议是避免添加特定于页面的脚本来运行特定于页面的代码。相反,将所有脚本包含在 application.js list 文件中,并使用页面上的元素来确定组件是否已挂载。你的例子在评论中做了这样的事情:
    document.addEventListener('turbolinks:load', () => {
    var element = document.getElementById("hello");
    if(element) {
    ReactDOM.render(<Hello name="React" />, element)
    }
    })

    如果它包含在您的 application.js 中,那么任何带有 #hello 的页面element 将获取组件。

    希望有帮助!

    关于ruby-on-rails - 在 Rails 5.1 中使用 Turbolinks 无法正确呈现 React 组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44604473/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com