Разработка React - компонентов

Чем отличается web-сайт от web-приложения?

контур.биллинг

cshtml

Server:

Client:

html
js

API

json

Server:

Client:

html
js
json

API

REACT

React

  • Declarative
  • Components
  • Virtual DOM

Веб-компоненты

< input  type= "radio"  name= "browser"  value= "opera" required >

< SuperComponent  doBest= "true" onDidBest= "doBetter()" >

Компонент

import React, { Component } from "react";
import Calendar from "rc-calendar";

class HelloUser extends Component {
    render() {
        return (
            <div className="hello">
                Hello {this.props.name}!
                <Calendar date={new Date()}/>
            </div>
        )
    }
}

ReactDOM.render(
    HelloUser,
    document.getElementById('root')
);

JSX

Чистый jS:

JSX:

React.createElement(
    "div",
    { className: "hello" },
    "Hello ",
    this.props.name,
    "!",
    React.createElement(Calendar, { date: new Date() })
);
<div className="hello">
    Hello {this.props.name}!
    <Calendar date={new Date()}/>
</div>

State

class UserForm extends Component {
    constructor(props) {
        super(props);
        this.state = {userName: ""}
    }

    _handleChange = (evt) => {
        this.setState({userName: evt.target.value})
    };

    render() {
        return (
            <div>
                Введите своё имя сюда:
                <input onChange={this._handleChange}/>
                <HelloUser name={this.state.userName}/>
            </div>
        )
    }
}
this.setState(newData)

this.state = {...this.state, ...data}

Компонент

  • Принимает props
  • Содержит state
  • Возвращает ReactElement

Lifecycle

  • void componentWillMount()
  • void componentDidMount()
  • void componentWillReceiveProps( object nextProps )
  • boolean shouldComponentUpdate(

​              object nextProps,

              object nextState

        )

  • void componentWillUpdate(object nextProps, object nextState )
  • void componentWillUnmount()

Virtual DOM

Real DOM

PATCH

Virtual DOM

Reconciliation​

Изменение типа ноды

A

B

<div>...</div>

<span>...</span>

[removeNode <div>...</div>],

[insertNode <span>...</span>]

render() {
    const isInline = this.state.isInline;
    return(
        { isInline ? <span>...</span> : <div>...</div> }
    )
}

Reconciliation​

Изменение типа компонента

A

B

<NewsList />

<ErrorMessage />

[removeNode <NewsList>],

[insertNode <ErrorMessage>]

render() {
    const hasNews= this.state.hasNews;
    return(
    { hasNews? <NewsList /> : <ErrorMessage /> }
    )
}

Reconciliation​

Изменение атрибутов

A

B

<div id="before"/>

<div id="after"/>

[replaceAttribute id "after"]

render() {
    const isBefore = this.state.isBefore;
    return(
    { <div id={ isBefore ? "before" : "after" } /> }
}

Reconciliation​

Компонент

?

class UserForm extends Component {
    constructor(props) {
        super(props);
        this.state = {userName: ""}
    }

    _handleChange = (evt) => {
        this.setState({userName: evt.target.value})
    };

    render() {
        return (
            <div>
                Введите своё имя сюда:
                <input onChange={this._handleChange}/>
                <HelloUser name={this.state.userName}/>
            </div>
        )
    }
}

Проблема

  • Компоненту пришли не изменившиеся props?

​         re-render!

  • state.value == 10

            setState({ value = 10 })

            re-render!!!

whyDidYouUpdate

import React from 'react'

if (process.env.NODE_ENV !== 'production') {
  const {whyDidYouUpdate} = require('why-did-you-update')
  whyDidYouUpdate(React)
}
class TextArea extends Component {
    constructor(props) {
        super(props);

        this.state = {
            height: props.minHeight
        };
    }

    _handleChange = (value, evt) => {
        const newHeight = this._calcHeight();
        this.setState({ height: newHeight })

        this.props.onChange(value, evt);
    };

    render() {
        const { height } = this.state;

        return (
            <textinput height={height}
                       onChange={this.handleChange}
                       value={this.props.value} />
        );
    }
}

Пример: TextArea

if (this.state.height != newHeight)
            this.setState({ height: newHeight });

Решение

import shouldPureComponentUpdate from 'react-pure-render/function';

class TextArea extends Component {
    shouldComponentUpdate = shouldPureComponentUpdate;
    ...
}

shouldPureComponentUpdate

function shouldPureComponentUpdate(nextProps, nextState) {
  return !(0, _shallowEqual2['default'])(this.props, nextProps) 
      || !(0, _shallowEqual2['default'])(this.state, nextState);
}

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

Реализация:

render() {
    return (
        <div>
            Введите своё имя сюда:
            <input onChange={() => {
                    this.setState({userName: evt.target.value});
                }}/>
            <HelloUser name={this.state.userName}/>
        </div>
    )
}
_handleChange(evt) {
    this.setState({userName: evt.target.value})
};

render() {
    return (
        <div>
            Введите своё имя сюда:
            <input onChange={(evt) => {this._handleChange(evt)}/>
            <HelloUser name={this.state.userName}/>
        </div>
    )
}
_handleChange = (evt) => {
    this.setState({userName: evt.target.value})
};

render() {
    return (
        <div>
            Введите своё имя сюда:
            <input onChange={this._handleChange}/>
            <HelloUser name={this.state.userName}/>
        </div>
    )
}

Как надо

Пример: Autocomplete

state = {
    "Options": [{
        "Text": "080075 - Алёна Санникова",
        "Value": "b47302b6-1e5e-44cc-830f-4c4ac2412516",
        "Description": null
    }, {
        "Text": "080097 - Крымский Александр Александрович",
        "Value": "5dacc4dd-2a9d-4c90-a8f0-e7b0ab15f186",
        "Description": null
    }, {
        "Text": "080091 - Истомин Константин Алекссевич",
        "Value": "c4ea9441-bcdf-434d-848c-7216857f0bf6",
        "Description": null
    },
        ...]
};
setState({
    "Options": [{
        "Text": "080075 - Алёна Санникова",
        "Value": "b47302b6-1e5e-44cc-830f-4c4ac2412516",
        "Description": null
    }, {
        "Text": "080097 - Крымский Александр Александрович",
        "Value": "5dacc4dd-2a9d-4c90-a8f0-e7b0ab15f186",
        "Description": null
    }, {
        "Text": "080091 - Истомин Константин Алекссевич",
        "Value": "c4ea9441-bcdf-434d-848c-7216857f0bf6",
        "Description": null
    },
        ...]
});

AJAX

Пример: UserList

Пример: UserList

"users": [{
    "Id": "a7e89cee-326f-48a6-a311-f34a8c3bad35",
    "Fio": "Олегович Чичерский Александр",
    "Thumbprint": "75fc92e743b33ee902caeb69d...",
    "Upto": "2014-07-31T00:00:00",
    "AccessToPlatforms": {
        "ManagerArm": false,
        "Support": true,
        "Oorv": false,
        "HealthMonitor": true
    }
},
...
{
    "Id": "667ae6ce-34fd-4546-be73-36774d7b139a",
    "Fio": "Санникова Алена Александровна",
    "Thumbprint": "51090110123903ACDDFB787E...",
    "Upto": "2013-03-13T00:00:00",
    "AccessToPlatforms": {
        "ManagerArm": false, 
        "Support": true, 
        "Oorv": false, 
        "HealthMonitor": false 
    }
},
...]
"users": [{
    "Id": "667ae6ce-34fd-4546-be73-36774d7b139a",
    "Fio": "Санникова Алена Александровна",
    "Thumbprint": "51090110123903ACDDFB787EB...",
    "Upto": "2013-03-13T00:00:00",
    "AccessToPlatforms": {
        "ManagerArm": false, 
        "Support": true, 
        "Oorv": false, 
        "HealthMonitor": false 
    }
}]

AJAX

state

new state

Решение

export const updateImmutableArrayByKey = (oldArray, newArray, key) => {
    const oldArrayHashmap = oldArray.reduce((result, item) => {
        result[item[key]] = item;
        return result;
    }, {});

    if (oldArray.length === newArray.length &&
        newArray.every((item) => oldArrayHashmap[item[key]])) {
        return oldArray;
    }

    return newArray.map((item) => oldArrayHashmap[item[key]] || item);
};

Стоит ли заморачиваться?

Гифки:

WarningMessage:

Lightbox:

TextArea:

UserList:

Календарь:

Стажировка

Спасибо за внимание

Вопросы?

REACT

By Andrey Osipov

REACT

  • 864