Skip to content

Commit f02d973

Browse files
authored
Generating function component by default (#1271)
* Add option for generating functional component * Change es6 template to a function component * Update tests * Update documentation * Update changelog
1 parent 4665a5f commit f02d973

8 files changed

+67
-31
lines changed

Diff for: CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ _Please add entries here for your pull requests that are not yet released._
1616
- Replaces WebpackManifestContainer, which searched for assets in the webpack manifest, with SeparateServerBundleContainer, which expects a single server bundle file & does not use the webpack manifest at all.
1717
- All in one PR! #1274
1818

19+
#### Changed
20+
- The `react:component` generator now generates a function component by default #1271
21+
1922
## [2.7.1] - 2023-05-19
2023

2124
#### Bug Fixes

Diff for: README.md

+31-2
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ Prerendering is set to `true` by default, but can be turned off with `prerender:
652652
You can generate a new component file with:
653653

654654
```sh
655-
rails g react:component ComponentName prop1:type prop2:type ...
655+
rails g react:component ComponentName prop1:type prop2:type ... [options]
656656
```
657657

658658
For example,
@@ -685,9 +685,38 @@ var Post = createReactClass({
685685

686686
The generator also accepts options:
687687

688-
- `--es6`: use `class ComponentName extends React.Component`
688+
- `--es6`: generates a function component
689689
- `--coffee`: use CoffeeScript
690690

691+
For example,
692+
693+
```sh
694+
rails g react:component ButtonComponent title:string --es6
695+
```
696+
697+
would generate:
698+
699+
```jsx
700+
import React from "react"
701+
import PropTypes from "prop-types"
702+
703+
function ButtonComponent(props) {
704+
return (
705+
<React.Fragment>
706+
Title: {this.props.title}
707+
</React.Fragment>
708+
);
709+
}
710+
711+
ButtonComponent.propTypes = {
712+
title: PropTypes.string
713+
};
714+
715+
export default ButtonComponent
716+
```
717+
718+
**Note:** In a Shakapacker project, es6 template is the default template in the generator.
719+
691720
Accepted PropTypes are:
692721

693722
- Plain types: `any`, `array`, `bool`, `element`, `func`, `number`, `object`, `node`, `shape`, `string`

Diff for: lib/generators/templates/component.es6.jsx

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
<%= file_header %>class <%= component_name %> extends React.Component {
2-
render () {
3-
return (
4-
<React.Fragment>
1+
<%= file_header %>
2+
const <%= component_name %> = (props) => {
3+
return (
4+
<React.Fragment>
55
<% attributes.each do |attribute| -%>
6-
<%= attribute[:name].titleize %>: {this.props.<%= attribute[:name].camelize(:lower) %>}
6+
<%= attribute[:name].titleize %>: {props.<%= attribute[:name].camelize(:lower) %>}
77
<% end -%>
8-
</React.Fragment>
9-
);
10-
}
8+
</React.Fragment>
9+
)
1110
}
1211

1312
<% if attributes.size > 0 -%>
@@ -17,4 +16,5 @@
1716
<% end -%>
1817
};
1918
<% end -%>
19+
2020
<%= file_footer %>

Diff for: lib/generators/templates/component.js.jsx

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
<%= file_header %>var <%= component_name %> = createReactClass({
1+
<%= file_header %>
2+
function <%= component_name %>(props) {
3+
return (
4+
<React.Fragment>
5+
<% attributes.each do |attribute| -%>
6+
<%= attribute[:name].titleize %>: {props.<%= attribute[:name].camelize(:lower) %>}
7+
<% end -%>
8+
</React.Fragment>
9+
);
10+
}
11+
212
<% if attributes.size > 0 -%>
3-
propTypes: {
13+
<%= file_name.camelize %>.propTypes = {
414
<% attributes.each_with_index do |attribute, idx| -%>
5-
<%= attribute[:name].camelize(:lower) %>: <%= attribute[:type] %><% if (idx < attributes.length-1) %>,<% end %>
15+
<%= attribute[:name].camelize(:lower) %>: <%= attribute[:type] %><% if (idx < attributes.length-1) %>,<% end %>
616
<% end -%>
7-
},
17+
};
818
<% end -%>
919

10-
render: function() {
11-
return (
12-
<React.Fragment>
13-
<% attributes.each do |attribute| -%>
14-
<%= attribute[:name].titleize %>: {this.props.<%= attribute[:name].camelize(:lower) %>}
15-
<% end -%>
16-
</React.Fragment>
17-
);
18-
}
19-
});
2020
<%= file_footer %>

Diff for: test/generators/coffee_component_generator_test.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def filename
1818

1919
es6 = File.read(File.join(destination_root, "app/javascript/components/GeneratedComponent.js"))
2020

21-
assert_match(/extends React.Component/, es6)
21+
assert_match(/const GeneratedComponent = \(props\) => {/, es6)
2222
end
2323
else
2424
def filename

Diff for: test/generators/component_generator_test.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,6 @@ def filename_with_subfolder
8686
run_generator %w[GeneratedComponent name:string address:shape]
8787
jsx = React::JSX.transform(File.read(File.join(destination_root, filename)))
8888

89-
assert_match(Regexp.new(expected_working_jsx), jsx)
89+
assert_match(Regexp.new(expected_working_jsx_in_function_component), jsx)
9090
end
9191
end

Diff for: test/generators/es6_component_generator_test.rb

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,26 @@ def filename
1818
end
1919
end
2020

21-
def class_name
21+
def component_name
2222
"GeneratedComponent"
2323
end
2424

2525
test "uses es6 syntax" do
2626
run_generator %w[GeneratedComponent name --es6]
2727

28-
assert_file filename, /^class\s#{class_name}\sextends\sReact\.Component/
28+
assert_file filename, /const #{component_name} = \(props\) => {/
2929
end
3030

31-
test "assigns defaultProps after class definintion" do
31+
test "assigns defaultProps after function definintion" do
3232
run_generator %w[GeneratedComponent name --es6]
3333

34-
assert_file filename, /\s^#{class_name}\.propTypes/
34+
assert_file filename, /\s^#{component_name}\.propTypes/
3535
end
3636

3737
test "generates working jsx" do
3838
run_generator %w[GeneratedComponent name:string address:shape --es6]
3939
jsx = React::JSX.transform(File.read(File.join(destination_root, filename)))
4040

41-
assert_match(Regexp.new(expected_working_jsx), jsx)
41+
assert_match(Regexp.new(expected_working_jsx_in_function_component), jsx)
4242
end
4343
end

Diff for: test/test_helper.rb

+4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ def expected_working_jsx
8585
/\.createElement\(\s*\S*\.Fragment,\s*null,\s*"Name:\s*",\s*this\.props\.name,\s*"Address:\s*",\s*this\.props\.address\s*\)/x # rubocop:disable Layout/LineLength
8686
end
8787

88+
def expected_working_jsx_in_function_component
89+
/\.createElement\(\s*\S*\.Fragment,\s*null,\s*"Name:\s*",\s*props\.name,\s*"Address:\s*",\s*props\.address\s*\)/x
90+
end
91+
8892
module ParamsHelper
8993
# Normalize params for Rails 5.1+
9094
def query_params(params)

0 commit comments

Comments
 (0)