Let's see how we can create an entity using the JHipster entity generator with a React client side. We will create a simple Employee entity with the name, age, and date of birth fields:
- Open a Terminal and navigate to the folder of the React app and run jhipster entity employee.
- Create the fields one by one, select yes for the question Do you want to add a field to your entity?, and start filling in the field name for the next question, What is the name of your field?
- Select String as the field type for the next question, What is the type of your field?
- For the question Which validation rules do you want to add?, choose Required for name field and proceed.
- Continue the process for the following fields. age and dob. age are of type Integer and dob is of type Instant.
- When asked again, Do you want to add a field to your entity?, choose no.
- For the next question, Do you want to add a relationship to another entity?, choose yes.
- Provide user as the name of the other entity and as the name of the relationship for the following questions.
- For the next question, What is the type of the relationship?, let's create a one-to-one relationship with the user.
- Choose no for the next question and no again when asked to add another relationship.
- For the following questions, select the default options and proceed.
The command will produce the following console output:
Using JHipster version installed globally
Executing jhipster:entity employee
Options:
The entity employee is being created.
...
================= Employee =================
Fields
name (String) required
age (Integer)
dob (Instant)
Relationships
user (User) one-to-one
? Do you want to use separate service class for your business logic? No, the REST controller should use the repository directly
? Do you want pagination on your entity? No
JHipster will generate the entity and run Prettier and the webpack build.
- If your server is not running, start it in a Terminal by running ./mvnw. If it is already running, then just compile the new code by running ./mvnw compile, and Spring DevTools will restart the app automatically.
- Start BrowserSync in another Terminal by running yarn start and check the employee entity we just created:

- Create an entity to check everything works fine:

For the entity we created, JHipster generated/updated the following files:

On the React client side, we have the following files:
src/main/webapp/app/entities/employee/employee-delete-dialog.tsx
src/main/webapp/app/entities/employee/employee-detail.tsx
src/main/webapp/app/entities/employee/employee-dialog.tsx
src/main/webapp/app/entities/employee/employee.tsx
src/main/webapp/app/entities/employee/employee.reducer.ts
src/main/webapp/app/shared/model/employee.model.ts
src/main/webapp/app/entities/employee/index.tsx
The index.ts file declares the routes for the entity:
<Switch>
<Route exact path={match.url} component={Employee} />
<ModalRoute exact parentPath={match.url} path={`${match.url}/new`}
component={EmployeeDialog} />
<ModalRoute exact parentPath={match.url} path={`${match.url}/:id/delete`}
component={EmployeeDeleteDialog} />
<ModalRoute exact parentPath={match.url} path={`${match.url}/:id/edit`}
component={EmployeeDialog} />
<Route exact path={`${match.url}/:id`} component={EmployeeDetail} />
</Switch>
employee.reducer.ts declares the actions and reducer for the entity, for example, let's use the action and reducer to create an entity. The createEntity action dispatches the ACTION_TYPES.CREATE_EMPLOYEE with the HTTP payload and metadata for notifications. Once the HTTP request resolves, we dispatch the getEntities action to fetch the updated entity list.
The reducer is common for create and update actions. Let's take a look at the create action and reducer:
...
export const ACTION_TYPES = {
...
CREATE_EMPLOYEE: 'employee/CREATE_EMPLOYEE',
...
};
const initialState = {
...
};
// Reducer
export default (state = initialState, action) => {
switch (action.type) {
...
case REQUEST(ACTION_TYPES.CREATE_EMPLOYEE):
...
return {
...
};
...
case FAILURE(ACTION_TYPES.CREATE_EMPLOYEE):
...
return {
...
};
...
case SUCCESS(ACTION_TYPES.CREATE_EMPLOYEE):
case SUCCESS(ACTION_TYPES.UPDATE_EMPLOYEE):
return {
...
};
...
default:
return state;
}
};
const apiUrl = SERVER_API_URL + '/api/employees';
...
export const createEntity: ICrudPutAction = entity => async dispatch => {
const result = await dispatch({
type: ACTION_TYPES.CREATE_EMPLOYEE,
meta: {
successMessage: messages.DATA_CREATE_SUCCESS_ALERT,
errorMessage: messages.DATA_UPDATE_ERROR_ALERT
},
payload: axios.post(apiUrl, entity)
});
dispatch(getEntities());
return result;
};
...
employee.tsx, employee-dialog.tsx, employee-detail.tsx, and employee-delete-dialog.tsx declare the entity listing, entity model dialog, entity detail, and entity delete dialog respectively. Let's look at employee.tsx, for example. We define the type for the props using a TypeScript interface, IEmployeeProps, which is passed as the generic for the React.Component type. We trigger the actions to fetch entities and users when our component mounts using the componentDidMount lifecycle method. The render method returns the JSX for the UI.
The component is connected to the Redux store using the higher-order component. Let's take a look:
...
export interface IEmployeeProps {
getEntities: ICrudGetAllAction;
employees: any[];
getusers: ICrudGetAllAction;
match: any;
}
export class Employee extends React.Component<IEmployeeProps> {
componentDidMount() {
this.props.getEntities();
this.props.getusers();
}
render() {
...
}
}
const mapStateToProps = storeState => ({
employees: storeState.employee.entities
});
const mapDispatchToProps = { getusers, getEntities };
export default connect(mapStateToProps, mapDispatchToProps)(Employee);
The other components also follow a similar approach. Codewise React code has much less boilerplate and is more concise compared to Angular.