React.js. Начало работы. Часть 5 из 12

18.10.2019

Теги: APIDOMJavaScriptReact.jsWeb-разработкаКомпонентФреймворк

Доступ к элементам DOM

Иногда необходимо напрямую обращаться к свойствам и методам HTML-документа. Потому что правильно реализовать такое обращение с использованием JSX и методов React сложно. Давайте рассмотрим простой пример палитры, которая окрашивает белый квадрат цветом, который указан в поле ввода.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>React</title>
    <script src="https://unpkg.com/react@16.0.0/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.0.0/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
    <style>
        .colorizer {
            margin: 20px;
        }
            .colorizer .colorSquare {
                box-shadow: 0 0 15px 0 #555555;
                width: 275px;
                height: 275px;
            }
            .colorizer input {
                padding: 10px;
                font-size: 16px;
                border: 2px solid #777777;
            }
            .colorizer button {
                padding: 10px;
                font-size: 16px;
                border: 2px solid #777777;
                background-color: #999999;
                color: #FFFFFF;
                margin: 10px;
            }
    </style>
</head>
<body>
    <div id="container"></div>
    <script type="text/babel">
    class Colorizer extends React.Component {
        constructor(props) {
            super(props);
            
            this.state = {
                color: "",
                bgColor: "white",
            }

            this.saveNewColor = this.saveNewColor.bind(this);
            this.paintNewColor = this.paintNewColor.bind(this);
        }
        
        saveNewColor(e) {
            this.setState({
                color: e.target.value
            });
        }
        
        paintNewColor(e) {
            this.setState({
                bgColor: this.state.color
            });
            e.preventDefault();
        }

        render() {
            var squareStyle = {
                backgroundColor: this.state.bgColor
            };
            return (
                <div className="colorizer">
                    <div style={squareStyle} className="colorSquare"></div>
                    <form onSubmit={this.paintNewColor}>
                        <input onChange={this.saveNewColor} placeholder="Значение цвета" />
                        <button type="submit">OK</button>
                    </form>
                </div>
            );
        }
    }

    ReactDOM.render(
        <Colorizer/>,
        document.querySelector('#container')
    );
    </script>
<body>
</html>

В этом приложении неудобно то, что когда кнопка получает фокус, введенное значение цвета остается в поле ввода. Если нужно ввести другое значение цвета, нужно вернуть фокус в текстовое поле и удалить старое значение. Гораздо удобнее, если бы введенное значение сбрасывалось и фокус возвращащался в текстовое поле сразу после нажатия кнопки ОК.

Использование ссылок

Правильно реализовать такое поведение с использованием JSX и традиционных методов React сложно. С другой стороны, сделать это с помощью API DOM JavaScript для HTML-элементов совсем просто. Для этого можно воспользоваться так называемыми ссылками (refs), доступными в React, чтобы получить доступ к HTML-элементам.

class Colorizer extends React.Component {
    /* .......... */
    render() {
        var squareStyle = {
            backgroundColor: this.state.bgColor
        };
        var self = this;
        return (
            <div className="colorizer">
                <div style={squareStyle} className="colorSquare"></div>
                <form onSubmit={this.paintNewColor}>
                    <input
                        onChange={this.saveNewColor}
                        ref={
                            function(element) {
                                self._input = element;
                            }
                        }
                        placeholder="Значение цвета" />
                    <button type="submit">OK</button>
                </form>
            </div>
        );
    }
}

Теперь у нас есть свойство _input объекта компонента. И через это свойство мы имеем доступ к текстовому полю. Так что можно установить фокус и сбросить введенное ранее значение цвета:

class Colorizer extends React.Component {
    /* .......... */
    paintNewColor(e) {
        this.setState({
            bgColor: this.state.color
        });
        // устанавливаем фокус на текстовое поле
        this._input.focus();
        // сбрасываем значение текстового поля
        this._input.value = "";
        e.preventDefault();
    }
    /* .......... */
}

Использование порталов

До сих пор мы вставляли JSX-код внутрь элемента страницы, который указывался вторым аргументом метода render():

ReactDOM.render(
    <Colorizer/>,
    document.querySelector('#container')
);

Но можно отображать JSX-контент внутри любого элемента страницы. Это функционал, известный как порталы. Добавим в наш пример заголовок и будем показывать в заголовке выбранный в палитре цвет.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>React</title>
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
    <style>
        #heading {
            margin: 20px;
            font-family: sans-serif;
        }
        .colorizer {
            margin: 20px;
        }
            .colorizer .colorSquare {
                box-shadow: 0 0 15px 0 #555555;
                width: 275px;
                height: 275px;
            }
            .colorizer input {
                padding: 10px;
                font-size: 16px;
                border: 2px solid #777777;
            }
            .colorizer button {
                padding: 10px;
                font-size: 16px;
                border: 2px solid #777777;
                background-color: #999999;
                color: #FFFFFF;
                margin: 10px;
            }
    </style>
</head>
<body>
    <h1 id="heading">Палитра</h1>
    <div id="container"></div>
    <script type="text/babel">
    class Colorizer extends React.Component {
        constructor(props) {
            super(props);
            
            this.state = {
                color: "",
                bgColor: "white",
            }

            this.saveNewColor = this.saveNewColor.bind(this);
            this.paintNewColor = this.paintNewColor.bind(this);
        }
        
        saveNewColor(e) {
            this.setState({
                color: e.target.value
            });
        }
        
        paintNewColor(e) {
            this.setState({
                bgColor: this.state.color
            });
            e.preventDefault();
        }

        render() {
            var squareStyle = {
                backgroundColor: this.state.bgColor
            };
            return (
                <div className="colorizer">
                    <div style={squareStyle} className="colorSquare"></div>

                    <form onSubmit={this.paintNewColor}>
                        <input onChange={this.saveNewColor} placeholder="Значение цвета" />
                        <button type="submit">OK</button>
                    </form>

                    <ColorLabel color={this.state.bgColor} />
                </div>
            );
        }
    }

    class ColorLabel extends React.Component {
        render() {
            var style = {
                color: this.props.color == "white" ? "lightgray" : this.props.color
            }
            return ReactDOM.createPortal(
                <span>
                    : <span style={style}>{this.props.color}</span>
                </span>,
                document.querySelector('#heading')
            );
        }
    }

    ReactDOM.render(
        <Colorizer/>,
        document.querySelector('#container')
    );
    </script>
<body>
</html>

Метод ReactDOM.createPortal() принимает два аргумента: JSX-код для вывода и элемент DOM для вывода этого кода. Мы добавляем к существующему содержимому заголовка JSX-контент, и в результате наш заголовок будет таким:

<h1 id="heading">
    Палитра<span>: <span style="color: green;">green</span></span>
</h1>

Поиск: API • DOM • JavaScript • React.js • Web-разработка • Фреймворк • Компонент

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.