1

I'm trying to include a react component dynamically using javascript i.e. a user can click "Add Place" and a form gets added to the page. This form contains a react component that uses material-ui's auto-complete component.

Below is the code for the form that gets added dynamically when button's clicked:

<div class="grid-x grid-margin-x grid-margin-y grid-padding-x grid-padding-y align-center">          
  <div class="cell medium-6">
        <div class="field">
          <%= f.label :activity_place %><br/>
          <%= react_component("PlaceSearch", {}, {prerender: true}) %>
        </div>
    </div>
    <div class="cell medium-6">
        <div class="field">
          <%= f.label :activity_time %><br/>
          <%= f.select(:activity_time, Constants::ACTIVITY_TIMES) %>
        </div>
    </div>
</div>

Below is the code for the Component (excluding imports):

class PlaceSearch extends React.Component {
    state = {
    checked: false,
    dataSource: [
      {
        text: 'text-value1',
        value: (
          <MenuItem
            primaryText="text-value1"
            secondaryText="&#9786;"
          />
        ),
      },
      {
        text: 'text-value2',
        value: (
          <MenuItem
            primaryText="text-value2"
            secondaryText="&#9786;"
          />
        ),
      },
    ]
  }


  render () {
    return (
      <MuiThemeProvider muiTheme={getMuiTheme({userAgent: (typeof navigator !== 'undefined' && navigator.userAgent) || 'all' })}>
        <div>
          <AutoComplete
            hintText="text-value data"
            filter={AutoComplete.noFilter}
            dataSource={this.state.dataSource}
          />
        </div>
      </MuiThemeProvider>
    );
  }
}

export default PlaceSearch

This example I've picked from material-ui's docs.

Everything works fine as expected except the react component. If I try to add the react component inclusion code somewhere else apart from the form being added dynamically, it works as expected.

I've searched different forums regarding material-ui and rails but could not find the solution to this particular problem.

VPaul
  • 1,005
  • 11
  • 21

2 Answers2

0

You should put your state declaration inside a constructor:

class PlaceSearch extends React.Component {
    constructor() {
        super();
        this.state = {
        checked: false,
        dataSource: [
           {
               text: 'text-value1',
               value: (
                   <MenuItem
                    primaryText="text-value1"
                    secondaryText="&#9786;"
                   />
               ),
           },
           {
               text: 'text-value2',
               value: (
                   <MenuItem
                    primaryText="text-value2"
                    secondaryText="&#9786;"
                   />
               ),
            }]   
         }
     }


  render () {
    return (
      <MuiThemeProvider muiTheme={getMuiTheme({userAgent: (typeof navigator !== 'undefined' && navigator.userAgent) || 'all' })}>
        <div>
          <AutoComplete
            hintText="text-value data"
            filter={AutoComplete.noFilter}
            dataSource={this.state.dataSource}
          />
        </div>
      </MuiThemeProvider>
    );   } }

export default PlaceSearch

If this works for you, this is why: SO Post

Martin Reiche
  • 1,642
  • 1
  • 16
  • 27
  • Thanks for the suggestion but your code gives the following error: Encountered error #" when prerendering PlaceSearch with {} – VPaul Apr 30 '18 at 07:14
  • Sorry, you should use this.state... my fault. See my updated answer – Martin Reiche Apr 30 '18 at 07:15
  • This seems to have resolved the error that your previous code caused but didn't made the component work as it should have i.e. the reason why the question was asked at the very first place. – VPaul Apr 30 '18 at 07:58
0

Finally made it working by adding manual component mounting code i.e. ReactRailsUJS.mountComponents() as mentioned in the github doc of the react-rails gem. Did no changes to the component JS file but only to the form that included it. The working code is below:

<div class="grid-x grid-margin-x grid-margin-y grid-padding-x grid-padding-y align-center">          
  <div class="cell medium-6">
        <div class="field">
          <%= f.label :activity_place %><br/>
          <%= react_component("PlaceSearch", {}, {prerender: true}) %>
        </div>
    </div>
    <div class="cell medium-6">
        <div class="field">
          <%= f.label :activity_time %><br/>
          <%= f.select(:activity_time, Constants::ACTIVITY_TIMES, include_blank: true) %>
        </div>
    </div>
</div>
<script>
    ReactRailsUJS.mountComponents()
</script>
VPaul
  • 1,005
  • 11
  • 21
  • I ran into issues later with this approach while using material-ui with react. I had to remove {prerender: true} from my component rendering view helper and rely solely on mountComponents() approach. Otherwise it was giving Client - Server className mismatch error. – VPaul May 21 '18 at 05:10