React-rails: How can I use a ReactComponent as a Layout ?

Created on 8 Jun 2018  Â·  8Comments  Â·  Source: reactjs/react-rails

if i write yield like this in application.html.slim , the other components will render out of the layout component

doctype html
html
  head
    title
      | Quiver
    = javascript_pack_tag 'application'
    = csrf_meta_tags
body
  = react_component('ApplicationLayout',{logo: asset_path('logo'))
  = yield 

Here is the description with a picture .

image

so ,is anybody can teach me how to do that? thanks .. :(

discussion

Most helpful comment

I didn't describe it clearly .
The requirements and descriptions are :

  • The project I'm developing is not a Single-Page-Application .
  • I am not using the react-router .
  • All page-jumping-actions managed by Rails but web-pages are rendered by React .
  • I hope to write a layout in application.html.slim .
  • I hope that it is a REACT COMPONENT not SIMPLE HTML .
  • Above is because that I'm using AntDesign .
  • I want to write Layout using AntDesign (That's why the Layout must be a REACT COMPONENT in my project).

It's something like this :

In application.html.slim .

  // If there is some helper can supply a yield in component .
  // For example , there is a helper named react_component_with_yield .

  = react_component_with_yield 'AppLayoutComponent' 

In Rails Controller FooController.rb

class FooController < ApplicationController 
    def bar
        @component_data = [];
    end
end

In app/views/foos/bar.html.slim:

= react_component 'BarComponent'

And the Component looks like :

AppLayout.js

... ...
import { Layout } from 'antd'
const {Content , Header ,Sider } = Layout

const AppLayout = () => {
    return(
        <Layout>
            <Header>
                header
            </Header>
            <Layout>
            <Sider/>
            <Content>
                // I want BarComponent to be here .
                // I know is wired but it's the only way i could thinkout
                // for example , there is a flag called rails_yield

                rails_yield

                // every slim-page where react_component helper is called will insert here .
                // repeat once again ,  i know it's wired ,but how can i make the Layout in application.html.slim to be a Component ?
            </Content>
            </Layout>
        </Layout>
    )
}

If i use the way u suggest , like :

  header 
    | this is header
  section
    = yield

It means that the layout is simple HTML TAGS but not react components . Also means that I can not use AntDesign's Layout components ... :(

Or . I have to import AppLayout Components in every Component Like This :

... ...
import AppLayout from './AppLayout'

const BarComponent = () => {
    return (
        <AppLayout>
            <div>
                bar bar bar 
            </div>
        </AppLayout>
    )
}

:(

Maybe it is the problem of my project-structure-design . It's a very old and large project , hard to refactor .

I think U are right . The react-rails gem is simple and easy to use . I have tried the rails_on_react , it's too complex for me .

I will keep importing AppLayout Components in every Component to keep development schedule until I find A better way . Or refactor the structure-design .

Thanks .

All 8 comments

I think there is some ways to do that.

(1) If you want to write the layout by using React Component, you should render your contents in the layout component. In this case, all props should be passed to the Layout componet at first and then you can pass some props to the child components.

<ApplicationLayout>
  <YourContent /> 
<ApplicationLayout/>

(2) You can also write the layout by slim and render some React components into it.

body
  header your header
  div
    main
      = react_component('YourContent')
    aside your aside
  footer your footer

I want it works like the first way but written by the second😂 .It makes me sad there's no option in react_component helper which supports insert a component to another . However I solved this by writing ApplicationLayout Component in every slim page . That is the first way you mentioned .

but written by the second

by writing ApplicationLayout Component in every slim page

Why?

insert a component to another

I don't think it's a responsibility of this gem. I want to keep this gem simple and if you want more complex one, you can use react_on_rails.

I think rendering a component in another isn't a responsibility of this gem but a responsibility of React itself. In my app, I'm always rendering App component in server side. Router is a component for client-side routing.

render component: 'App', props: props
function App(props) {
  const store = buildStore(props);

  return (
    <Provider store={store}>
      <Router />
    </Provider>
  );
}

by writing ApplicationLayout Component in every slim page

Oh, at least you don't need to write a layout in every slim page. How about this?

# application.html.slim

(snip)
body
  header your header
  div
    main
      = yield
    aside your aside
  footer your footer
# foo.html.slim

= react_component('YourContent')

I didn't describe it clearly .
The requirements and descriptions are :

  • The project I'm developing is not a Single-Page-Application .
  • I am not using the react-router .
  • All page-jumping-actions managed by Rails but web-pages are rendered by React .
  • I hope to write a layout in application.html.slim .
  • I hope that it is a REACT COMPONENT not SIMPLE HTML .
  • Above is because that I'm using AntDesign .
  • I want to write Layout using AntDesign (That's why the Layout must be a REACT COMPONENT in my project).

It's something like this :

In application.html.slim .

  // If there is some helper can supply a yield in component .
  // For example , there is a helper named react_component_with_yield .

  = react_component_with_yield 'AppLayoutComponent' 

In Rails Controller FooController.rb

class FooController < ApplicationController 
    def bar
        @component_data = [];
    end
end

In app/views/foos/bar.html.slim:

= react_component 'BarComponent'

And the Component looks like :

AppLayout.js

... ...
import { Layout } from 'antd'
const {Content , Header ,Sider } = Layout

const AppLayout = () => {
    return(
        <Layout>
            <Header>
                header
            </Header>
            <Layout>
            <Sider/>
            <Content>
                // I want BarComponent to be here .
                // I know is wired but it's the only way i could thinkout
                // for example , there is a flag called rails_yield

                rails_yield

                // every slim-page where react_component helper is called will insert here .
                // repeat once again ,  i know it's wired ,but how can i make the Layout in application.html.slim to be a Component ?
            </Content>
            </Layout>
        </Layout>
    )
}

If i use the way u suggest , like :

  header 
    | this is header
  section
    = yield

It means that the layout is simple HTML TAGS but not react components . Also means that I can not use AntDesign's Layout components ... :(

Or . I have to import AppLayout Components in every Component Like This :

... ...
import AppLayout from './AppLayout'

const BarComponent = () => {
    return (
        <AppLayout>
            <div>
                bar bar bar 
            </div>
        </AppLayout>
    )
}

:(

Maybe it is the problem of my project-structure-design . It's a very old and large project , hard to refactor .

I think U are right . The react-rails gem is simple and easy to use . I have tried the rails_on_react , it's too complex for me .

I will keep importing AppLayout Components in every Component to keep development schedule until I find A better way . Or refactor the structure-design .

Thanks .

The way I see it a potential solution is that you sniff the ant design rendered HTML. After all React is rendered out to simple HTML tags. So you grab the ant design HTML structure, add ant design CSS to your layout and then use the React components where they are necessary.

@qxchen6

"Simplest" though definitely not best practice:

Create a layout ReactComponent which renders passed in HTML in a prop as dangerouslySetInnerHTML.

Again, not best practice.

= react_component("ReactLayout", content: yield)

class ReactLayout extends React.Component {
  render() {
    return <div dangerouslySetInnerHTML={{ _html: this.props.content }}>
    </div>
  }
}

On second thought, yield might not work as is. If it doesn't try render_to_string(partial: "yield") then have a app/views/application/_yield.html.erb which just contains yield.

Going to close this to cleanup open discussions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hugofloss picture hugofloss  Â·  4Comments

rstudner picture rstudner  Â·  5Comments

mmccall10 picture mmccall10  Â·  4Comments

adoseofjess picture adoseofjess  Â·  4Comments

okolomoets picture okolomoets  Â·  5Comments