Ładowanie CSS i CSS modules w tym samym czasie Webpack

W react możemy importować css jak i css jako moduły.
Ale co jeśli chcemy zrobić do w tym samym czasie powiedzmy dodać globalny styl np. normalize.css?

Importowanie jako moduł:

import React from 'react'
import styles from './moj-komponent.css';

const MyComponent => <div className={styles.jakasKlasa}></div>

No ale jak użyć css powiedzmy jakieś zewnętrznej biblioteki:

import React from 'react'
import './moj-komponent-zewnetrzny.css';

const MyComponent => <div className="jakasKlasa"></div>

Aby tego dokonać należy zmodyfikować webpack. Ja korzystam z create-react-app i modyfikuję zarówno wersję dev jak i prod webpacka.

module: {
    rules: [
        {
            // wcześniej było /\.css$/
            test: /^(?!.*?\.module).*\.css$/,
           
            // dalej zostawiamy jak było czyli
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1,
                  modules: true,
                  localIdentName: '[name]__[local]__[hash:base64:5]'
                },
                ...
        },
        // dodajemy następną regułę dla zewnętrznych css
        {
            test: /\.module\.css$/,
            use: [
                require.resolve('style-loader'),
                require.resolve('css-loader')
            ],
        },
    ]
}

Teraz możemy użyć w jednym komponencie zarówno css modułów jak i zewnętrznych bibliotek.
Należy pamiętać że zewnętrzny komponent musi mieć w nazwie *.module.css

import React from 'react'
import styles from './moj-komponent.css';
import './jakis-zewnetrzny.module.css'

const MyComponent => <div className={`${styles.jakasKlasa} jakas-klasa-z-zewnatrz`}></div>

W wersji produkcyjnej webpack kopiuję całą regułę css i wstawiam poniżej,
zmieniam test: /\.css$/, na test: /\.module\.css$/,
Ustawiam modules: false i tyle.

Importowanie absolutne w Create React App

Jak pozbyć się zagnieżdżonych ścieżek w pejekcie.
Np. taka nieładna ścieżka jak poniżej.

import TopImage from '../../../components/TopImage/TopImage';

Chcemy mieć coś takiego, czy do foldera components czy do containers czy innych folderów w src.

import TopImage from 'components/TopImage/TopImage';

Najprościej jak można to tworzymy plik w folderze src o nazwie .env tak z kropką na początku.
W nim wpisujemy NODE_PATH=src/ robimy save i uruchamiamy project jak zwykle przez npm run start

Od tej chili zamiast podawać całą ścieżkę podajemy components, containers, hoc czy inne foldery jakie mamy w projekcie.

Innym sposobem jest modyfikacja package.json

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build"
}

Dodajemy NODE_PATH=src/

"scripts": {
   "start": "NODE_PATH=src/ react-scripts start",
   "build": "NODE_PATH=src/ react-scripts build"
}

Jest jeden problem z użytkownikami windowsa jak zwykle 😉 NODE_PATH=src/ w package.json nie zadziała.
Należy zainstalować moduł cross-env czyli npm install –save-dev cross-env i zmodyfikować package.json, oczywiście to tylko w wypadku windowsa.

"scripts": {
   "start": "cross-env NODE_PATH=src/ react-scripts start",
   "build": "cross-env NODE_PATH=src/ react-scripts build"
}

I to tyle chyba ten pierwszy sposób jest mniej inwazyjny i powinien zadziałać na wszystkich platformach.

Wysłanie formularza w React

Proste wysłanie formularza w React do tego axios czyli biblioteka oparta na obiektach HTTP dla przeglądarek i Node.js więcej można przeczytać tutaj – axios

import React, { Component } from 'react';
import axios from 'axios';

class UserForm extends Component {
    state = {
        imie: '',
        nazwisko: '',
        email: '',
    };

    onChange = (event) => {
        const state = this.state
        state[event.target.name] = event.target.value;
        this.setState(state);
    }

    onSubmit = (event) => {
        event.preventDefault();
        const { imie, nazwisko, email } = this.state;

        axios.post('/', { imie, nazwisko, email })
            .then((result) => {
                // a tuj coś robimy z danymi
            });
    }

    render() {
        const { imie, nazwisko, email } = this.state;
        return (
            <form onSubmit={this.onSubmit}>
                <input type="text" name="imie" value={imie} onChange={this.onChange} />
                <input type="text" name="nazwisko" value={nazwisko} onChange={this.onChange} />
                <input type="text" name="email" value={email} onChange={this.onChange} />
                <button type="submit">Wyślij</button>
            </form>
        );
    }
}

export default UserForm;

show/hide scroll React

Ukrywanie buttona scroll-top jeżeli wysokość przekroczy 200 oraz dodanie obsługi zdarzenia na onClick i wywołanie funkcji scrollToTop i metody scrollIntoView.

export default BackToTop extends Component {
    state = {
        scrolling: false,
        scrollPosHeight: 200
    }

    componentDidMount() {
        console.log('componentDidMount()');
        window.addEventListener('scroll', this.hideShowButtonTop);
    };

    componentWillUnmount() {
        console.log('componentWillUnmount()');
        window.removeEventListener('scroll', this.hideShowButtonTop);
    };

    hideShowButtonTop = () => {
        const scrollPos = window.pageYOffset || document.documentElement.scrollTop;
        const myDiv = document.querySelector('.back-to-top');
        (scrollPos > this.state.scrollPosHeight && scrollPos !== 0) ? myDiv.style.display = 'block' : myDiv.style.display = 'none';
    }

    scrollToTop() {
        const logoView = document.getElementById('logo');
        logoView.scrollIntoView({
            behavior: 'smooth'
        })
    }

    render() {
        return (
            <div onClick={this.scrollToTop} className="back-to-top"></div>
        )
    };
};