Building ****** Finder 2.0
My experience with React & Co in a large-scale application
src ├── screens │ └── App │ ├── components │ ├── screens │ │ ├── Admin │ │ │ ├── components │ │ │ ├── screens │ │ │ │ ├── Reports │ │ │ │ │ ├── components │ │ │ │ │ └── Reports.screen.tsx │ │ │ │ └── Users │ │ │ │ ├── components │ │ │ │ └── Users.screen.tsx │ │ │ ├── shared │ │ │ │ └── helpers │ │ │ │ └── some.helper.ts │ │ │ └── Admin.screen.tsx │ │ └── Course │ │ ├── components │ │ ├── screens │ │ │ └── AssignmentsList │ │ │ ├── components │ │ │ └── AssignmentsList.screen.tsx │ │ └── Course.screen.tsx │ ├── shared │ │ └── components │ │ ├── Avatar.component.tsx │ │ └── Icon.component.tsx │ └── App.tsx ├── shared └── index.ts
- mirrors route configuration
- screens act as containers
- multiple /shared folders for each section
- easy maintenance and reusability
- long readable filenames
- avoid non-descriptive "index.js"
- suffix helps a lot
File naming
VehicleList.screen.tsx VehicleListFilter.container.tsx VehicleListFilter.component.tsx vehicleListFilter.scss VehicleListFilterChips.component.tsx preventTouchClickMouseEmulation.helper.ts
CSS Modules + SASS
$breakpoints: (
xxs: 0px,
xs: 480px,
s: 768px,
m: 992px,
l: 1200px,
xl: 1600px
);
$brand-blue: #15577e;
$weight-light: 300;
$font-size-display: (
xxs: (
font-size: 1.900rem,
line-height: 2.375rem,
font-weight: $weight-light
),
xs: (
font-size: 2.000rem,
line-height: 2.500rem,
font-weight: $weight-light
)
);
Our own responsive grid library
typed interfaces instead of prop-types
interface PropsInterface {
count: number;
options: Array<string>;
label?: string | JSX.Element;
type?: 'default' | 'primary' | 'thin';
entity: AsyncModel<InquiryDetails>;
filterKey: keyof OfferFilter;
}
const SomeComponent: React.SFC<PropsInterface> = (props) => {
return ...
};
swagger-codegen for models
export interface OfferDetails {
"financingEnabled"?: boolean;
"includedWarranty"?: string;
"leaseAnnualMileage"?: string;
"leaseDownPayment"?: PriceView;
"leaseDuration"?: string;
"leaseRate"?: PriceView;
"leasingEnabled"?: boolean;
"maintenanceBeforeDeliver"?: boolean;
"marketPlace"?: OfferMarketPlaceView;
"offerKey": string;
"price"?: PriceView;
"registrationBeforeDelivery"?: boolean;
"totalPrice"?: PriceView;
"vehicle": VehicleOfferDetails;
"warrantyOffers"?: Array<WarrantyOfferView>;
}
swagger-codegen for API
create(params: { "create": VehicleCreate; }, options?: any): (fetch?: FetchAPI, basePath?: string)
=> Promise<VehicleDetails> {
const fetchArgs = VehicleApiFetchParamCreator.create(params, options);
return (fetch: FetchAPI = isomorphicFetch, basePath: string = BASE_PATH) => {
return fetch(basePath + fetchArgs.url, fetchArgs.options).then((response) => {
if (response.status >= 200 && response.status < 300) {
return response.json();
} else {
throw response;
}
});
};
}
typings-for-css-modules-loader
.attribute-circle {
...
&-icon {
...
}
&--inner {
...
}
}
attributeCircle.scss
attributeCircle.scss.d.ts
export const attributeCircle: string;
export const attributeCircleInner: string;
export const attributeCircleIcon: string;
Autocomplete for classnames
Using nonexistent/renamed classnames results in typescript warning
"Dead" class detection
Webpack configuration
- bootstrapped using create-react-app
- typescript flavour
- aliases for shorter import statements
- happypack
import FlexItem from '../../../../../../../../shared/components/...'
import FlexItem from 'shared/components/...'
react-responsive for complex responsive functionality
react-intl for i18n
Infinite scrolling and Back button
admin interface
- CRUD
- > 40 business objects
- consistent UI and data structure (API and DTOs)
- consistent URL scheme
- base screens and components with common interface -> TypeScript FTW!!!!1!!111!!111eleven
- heavy use of TypeScript generics
- no Redux (too much overhead, not much benefits)
list
filter
- React form components
- filter definitions from server (visible, possible options, validation, current value)
table
- column definitions
- paging
- sorting (server side)
- links to view, edit, delete
- loader container
- filter values, paging and sorting in URL -> deep links, browser navigation
- data loading
- flash messages for error and success
list component structure
ModelGenerationListScreen
-
AdminEntityListScreen
-
ModelGenerationFilter
-
AdminEntityFilter
- form components
-
AdminEntityFilter
-
ModelGenerationList
-
PagedAdminEntityTable
-
AdminEntityTable
- headers
- rows
- columns
- cell renderers
- Pager
- LoaderContainer
-
AdminEntityTable
-
PagedAdminEntityTable
-
ModelGenerationDeleteConfirmation
- DialogConfirmation
-
ModelGenerationFilter
ModelGenerationListScreen extends AdminEntityListScreen
-
ModelGenerationFilter extends AdminEntityFilter
- form components
-
ModelGenerationList extends PagedAdminEntityTable
-
AdminEntityTable
- cell renderers
- columns
- rows
- headers
- Pager
- LoaderContainer
-
AdminEntityTable
- ModelGenerationDeleteConfirmation extends DialogConfirmation
no extends in React!
list component structure
excursus
React components with Generics
generic React component
works but no warning in IDE / compiler
generic React component
won't work (JSX syntax error)
generic React component
real world example
list component structure
view
- simple properties
- linked references to other business objects
- i18n properties
- link to delete and edit
- loader container
- data loading
- flash messages for error and success
view component structure
ModelGenerationViewScreen
-
AdminEntityViewScreen
-
ModelGenerationView
- Rows
- Columns
- Property Renderer
- AdminL10NViewRow
- LoaderContainer
-
ModelGenerationDeleteConfirmation
- DialogConfirmation
-
ModelGenerationView
ModelGenerationViewScreen extends AdminEntityViewScreen
-
ModelGenerationView
- Rows
- Columns
- Property Renderer
- AdminL10NViewRow
- LoaderContainer
- ModelGenerationDeleteConfirmation extends DialogConfirmation
no extends in React!
view component structure
create / edit
- form components for simple properties, i18n properties or linked references
- form validation
- form field definition from server (visible, readonly, possible options, validation, current value)
- data loading and saving
- flash messages for error and success
create component structure
ModelGenerationCreateScreen
-
AdminEntityCreateScreen
-
ModelGenerationForm
- Rows
- Columns
- Form components
- AdminL10NFormRow
- LoaderContainer
-
ModelGenerationForm
ModelGenerationCreateScreen extends AdminEntityCreateScreen
-
ModelGenerationForm
- Rows
- Columns
- Form components
- AdminL10NFormRow
- LoaderContainer
no extends in React!
edit component structure
ModelGenerationEditScreen
-
AdminEntityEditScreen
-
ModelGenerationForm
- Rows
- Columns
- Form components
- AdminL10NFormRow
- LoaderContainer
-
ModelGenerationForm
ModelGenerationEditScreen extends AdminEntityEditScreen
-
ModelGenerationForm
- Rows
- Columns
- Form components
- AdminL10NFormRow
- LoaderContainer
no extends in React!
create component structure
edit component structure
wiring it together
- CRUD screen for every business object with subscreens for list, view, edit, create
- constistens URL scheme
- component structure
- ModelGenerationsScreen
- AdminEntityCRUD
- ModelGenerationListScreen
- ModelGenerationViewScreen
- ModelGenerationEditScreen
- ModelGenerationCreateScreen
- AdminEntityCRUD
- ModelGenerationsScreen
directory structure
directory structure
Then God blessed them and said, "Be fruitful and multiply. Fill the earth and govern it. Reign over the fish in the sea, the birds in the sky, and all the animals that scurry along the ground."
Book of Genesis 1:28
benefits
- ca. 1h per business object
(copy & paste & search & replace & modify) - very short ramp up time for new developers
- refactoring very easy
(and done very often) - backend modifications can be detected
- consistent UI and URL scheme
codeStyleSettings.xml
Problems
nonexistent typescript typings
- Webpack incremental build performance
- Large action and reducer files
- shouldComponentUpdate
- Redux + TypeScript = even more boilerplate
- dirty hack for React components with TypeScript generics
- no tests
Would we use this stack again?
Geschafft!
Building ****** Finder
By Sergey Ryzhov
Building ****** Finder
- 1,494