Application Customization

In the Application.tsx file, you can customize the application by adding custom reducers, components, and icons. Here is how the file looks in the boilerplate:

import useApplication from '@steroidsjs/core/hooks/useApplication';
import HttpComponent from '@steroidsjs/core/components/HttpComponent';
import LocaleComponent from '@steroidsjs/core/components/LocaleComponent';
import customIcons from 'icons/index';

import 'style/index.scss';

export default function Application() {
    const {renderApplication} = useApplication({
        reducers: require('@steroidsjs/core/reducers').default,
        routes: () => require('routes').default,
        layoutView: () => require('shared/Layout').default,
        screen: {},
        components: {
            locale: LocaleComponent,
            http: HttpComponent,
        },
        onInit: ({ui}) => {
            ui.addViews(require('./ui/bootstrap').default);
            ui.addFields(require('@steroidsjs/core/ui/form').default);
            ui.addFormatters(require('@steroidsjs/core/ui/format').default);
            ui.addIcons(require('@steroidsjs/bootstrap/icons/index').default(customIcons));
        },
    });

    return renderApplication();
}

Adding custom reducers

To add a reducer to the application, you need to follow several steps:

1. Create a folder named src/reducers/.

2. Create a file named index.ts inside this folder and export a function in it that combines all reducers from Steroids.

Here's an example of the code:

import steroidsReducers from '@steroidsjs/core/reducers';

export default asyncReducers => steroidsReducers({
    ...asyncReducers,
});

3. Create the necessary custom reducers in the src/reducers/ folder, with each reducer in a separate file, and add them to the object of reducers passed as the first argument to the combineReducers function.

4. In the Application.tsx file, change the path to the reducers file from @steroidsjs/core/reducers to ./reducers.

Adding Custom Icons

By default, Steroids comes with a set of icons for use. The list of provided icons can be found in the react-bootstrap repository under the src/icons/svgs folder.

To add custom icons, follow these steps:

1. Add the icon to your project, typically in the src/icons/svgs folder.

2. Create a file named index.ts in the src/icons/ folder. In this file, export an object that describes the configuration for the icons. The key is the name you will use to reference the icon in your application, and the value is the path to the icon file.

Example code:

export const icons = {
    kozhinDev: require('./svgs/kozhinDev.svg'),
    logo: require('./svgs/logo.svg'),
    light: require('./svgs/light.svg'),
    dark: require('./svgs/dark.svg'),
};

3. Now, you can use the added icons in your application by referencing them by name:

import Icon from '@steroidsjs/core/ui/content/Icon';

export default () => (
    <Icon name='kozhinDev' />
);

Adding Custom Components

Steroids provides a set of components for working with Redux, handling backend requests, localization, etc.

In addition to using the existing components, you can also:

  • Extend the functionality of an existing component by adding business logic for a specific project.
  • Add your own component and pass it the necessary props.

We will consider each method separately below.

1. Extending the functionality of an existing component

For example, let's extend the functionality of the HttpComponent. Let's say we want to add the Accept-Language header to the request to the backend for localization of the response from the server.

First, we create the HttpComponent component in the src/components/ folder. This component will extend the built-in Steroids component by adding the Accept-Language header to the _send method.

import SteroidsHttpComponent from '@steroidsjs/core/components/HttpComponent';

export default class HttpComponent extends SteroidsHttpComponent {
    _send(method: string, config?: Record<string, any>, options?: Record<string, any>) {
        const configWithLanguage = {
            headers: {
                'Accept-Language': this._components.locale.language,
            },
            ...config,

        };
        
        return super._send(method, configWithLanguage, options);
    }
}

Next, we connect the extended component to the Application.tsx configuration.

Example code:

import useApplication from '@steroidsjs/core/hooks/useApplication';
import LocaleComponent from '@steroidsjs/core/components/LocaleComponent';
import HttpComponent from './components/HttpComponent'; // import the component

import 'style/index.scss';

export default function Application() {
    const {renderApplication} = useApplication({
        reducers: require('@steroidsjs/core/reducers').default,
        routes: () => require('routes').default,
        layoutView: () => require('shared/Layout').default,
        screen: {},
        components: {
            locale: {
                className: LocaleComponent,
            },
            http: {
                className: HttpComponent, // connect the custom component
                apiUrl: process.env.APP_BACKEND_URL, // additionally specify the URL address
            },
        },
        onInit: ({ui}) => {
            ui.addViews(require('./ui/bootstrap').default);
            ui.addFields(require('@steroidsjs/core/ui/form').default);
            ui.addFormatters(require('@steroidsjs/core/ui/format').default);
            ui.addIcons(require('@steroidsjs/bootstrap/icons/index').default(customIcons));
        },
    });

    return renderApplication();
}

Note: if you don't need to pass additional props to the component, you can use the short notation.

Example code:

import useApplication from '@steroidsjs/core/hooks/useApplication';
import HttpComponent from './components/HttpComponent';

export default function Application() {
    const {renderApplication} = useApplication({
        components: {
            http: HttpComponent,
        },
    });

    return renderApplication();
}

After configuring the custom component, you can access it using the useComponents hook by specifying its name, just like when using a default component from Steroids.

Below is an example of using the http component to retrieve a list of comments:

import {useEffect, useState} from 'react';
import {useComponents} from '@steroidsjs/core/hooks';
import Comment from './views/Comment';
import API_METHODS from '../../config/api';

import './Comments.scss';

interface ICommentsProps {
    userId: number;
}

export default function Comments(props: ICommentsProps) {
    const [comments, setComments] = useState([]);
    const {http} = useComponents();

    useEffect(() => {
        http.get(API_METHODS.COMMENTS, {userId: props.userId})
            .then((response) => setComments(response?.items));
    }, [http, props.userId]);

    return (
        <div className'Comments'>
            {
                comments.map(comment => (
                    <Comment
                        key={comment.id}
                        text={comment.text}
                    />
                ))
            }
        </div>
    );
}

2. Adding your own component

In addition to extending the functionality of existing components, we can also add our own.

To achieve this, let's create a custom component in the src/components/ folder, for example, ConverterComponent, for currency conversion from rubles to dollars.

Example code:

export default class ConverterComponent {
    rub: number;

    course: number;

    constructor(components, config: any = {}) {
        this.rub = config.initialRubValue;
        this.course = config.course;
    }

    rubToUsd(rub: number) {
        return Number(((rub || this.rub) / this.course).toFixed(2));
    }
}

To use the ConverterComponent, you need to connect it to your application's configuration in the Application.tsx file. The following code snippet shows how to do this:

import useApplication from '@steroidsjs/core/hooks/useApplication';
import Converter from './components/Converter';

import 'style/index.scss';

export default function Application() {
    const {renderApplication} = useApplication({
        reducers: require('@steroidsjs/core/reducers').default,
        routes: () => require('routes').default,
        layoutView: () => require('shared/Layout').default,
        screen: {},
        components: {
            converter: {
                className: Converter,
                initialRubValue: 100,
                course: 89,
            },
        },
        onInit: ({ui}) => {
            ui.addViews(require('./ui/bootstrap').default);
            ui.addFields(require('@steroidsjs/core/ui/form').default);
            ui.addFormatters(require('@steroidsjs/core/ui/format').default);
            ui.addIcons(require('@steroidsjs/bootstrap/icons/index').default(customIcons));
        },
    });

    return renderApplication();
}

Once you have connected the ConverterComponent to your application, you can use it in your components. For example, the following code snippet shows how to use it to create a form for converting currencies:

import {useEffect} from 'react';
import {useComponents, useDispatch, useSelector} from '@steroidsjs/core/hooks';
import {formChange} from '@steroidsjs/core/actions/form';
import {formSelector} from '@steroidsjs/core/reducers/form';
import {Form, NumberField} from '@steroidsjs/core/ui/form';

const FORM_ID = 'ConverterForm';

export default function ConverterForm() {
    const dispatch = useDispatch();
    const {converter} = useComponents();
    const formValues = useSelector(state => formSelector(state, FORM_ID, ({values}) => values));

    useEffect(() => {
        const usd = converter.rubToUsd(Number(formValues?.rub));
        dispatch(formChange(FORM_ID, 'usd', usd));
    }, [converter, dispatch, formValues?.rub]);

    return (
        <Form
            formId={FORM_ID}
            useRedux
            initialValues={{
                rub: converter.rub,
            }}
        >
            <NumberField
                attribute='rub'
                label='Rubles'
            />
            <NumberField
                attribute='usd'
                label='Dollars'
                disabled
            />
        </Form>
    );
}