React

interview

React.js - это библиотека разработанная фирмой Facebook, которая отвечает за слой представления (View) для приложений построенных на архитектуре Flux.
Основным преимуществом данной библиотеки является VirtualDOM - виртуальный DOM. Т.к. перерисовка DOM одна из самых затратных операций, React реализует следующий алгоритм для решения этой проблемы: при каких либо изменениях интерфейса в результате действий пользователя, с начало создается виртуальный DOM, который сравнивается с текущим - и перерисовывается только та часть, которая действительно изменилась.

Это также является и своего рода недостатком, т.к. в памяти теперь нужно держать 2 дерева DOM (реальный и виртуальный). Также реализация построения виртуального DOM - требует затрат времени.

Основой библиотеки является Компонент. Главный метод компонента это render(). Создается следующим образом:

  //ES5
  var Comment = React.createClass({
   render(){
    return <div> ... </div>
   }
  });

  //ES6
  class Comment extends React.Component{
   constructor(props){
    super(props);
    ...
   }

   render(){
    return <div>...</div>
   }
  }

  //функциональный стиль (для stateless-компонентов)
  const MyComponent = (props) => {
   return <h1>...</h1>
  }

Атрибуты компонента:

  • key - уникальный ключ компонента, например если у нас есть компонент-родитель, который отрисовывает компоненты-дети через функция map - то необходимо чтобы у каждого компонента-ребёнка был свой уникальный ключ.
  • ref - это специальное свойство, которое мы можем прикрепить к любому компоненту, который возвращается из render(). Это особое свойство позволяет обратиться к возвращаемому экземпляру(Помните: что вы возвращаете из render() не фактические экземпляры, а лишь описание экземпляра). Он всегда гарантирует правильный экземпляр, в любой момент времени.

Компонент содержит:

  • props - это данные приходящие от родительского компонента или из стора, они readonly - поэтому мы не можем их изменять внутри компонента, только по средствам callback-ов
  • state - это внутреннее состояние компонента, можно изменять с помощью this.setState({...}), использовать данный метод нужно с осторожностью, например если использовать внутри render - тогда при отрисовки компонента, react увидит что state изменился - перерисует - опять увидит что изменился - перерисует - ... Также его можно использовать только в 4 методах жизненого цикла компонента (см. ниже)
    this.setState((prevState, props) => {...});
    // стоит избегать использования this.state внутри setState, т.к. this.state
    // не гарантирует что мы получим консистентнные данные. Поэтому лучше использовать 
    // функцию с 2 аргументами prevState - пред.состояние и props - внешние данные.
    
  • propsType - это валидация props (number, string, boolean, function, any,...)

    MyComponent.propsType = {
    id: React.propsTypes.number.isRequired, //тип число, обязательное присутствие
    name: React.propsTypes.string, // тип строка
    age: React.propsTypes.oneOfTypes({ //может быть как числом, так и строкой
    React.propsType.number,
    React.propsType.string
    }),
    object: React.propsType.share({
    name: React.propsType.string
    age: React.propsType.number
    }), 
    }
    

Про setState

  1. вызывает re-render компонента (исключение - если используется shouldComponentUpdate)
  2. обновление выполняется не моментально, react может отложить выполнения (оптимизация, частая ошибка - вызов this.state сразу после this.setState())

Вторым необязательным аргументом функции setState является функция callback, которая будет вызвана (гарантировано) только после того как компонент переотрисуется (re-rendered).

handleClick = () => {
 this.setState({...}, () => {...});
}

Жизненый цикл компонента:
lifecycle

  • Mount
    • constructor (или getDefaultProps, getInitialState)
    • componentWillMount (setState)
    • render
    • componentDidMount (setState)
  • Update
    • componentWillReceiveProps(setState)
    • shouldComponentUpdate - компонент используется для оптимизации, можно реализовать свою логику проверки - изменилось ли действительно состояние хранилища или нет. Но по умолчанию производится неглубокое сравнение (return !shallowEqual(this.props, nextProps)) - аналогично можно унаследоваться от PureComponent - он будет проверять props и state из коробки
    • componentWillUpdate
    • render
    • componentDidUpdate (setState)
  • Unmount
    • componentWillUnmount

Примечание: в eslint есть правило что нельзя вызывать setState в методе componentDidMount:

Я так понял это правило – чтобы избавится от лишних и бесполезных вызовов render-ов. Метод componentDidMount вызывается после первого вызова render(), а тут мы меняем состояние через setState – в результате будет снова вызван render() (такие методы жизненного цикла как shouldComponentUpdate, WillUpdate будут пропущены). А если у нас такое же поведения в child-компонентах – то они будут перерисовываться 4 раза. И чем глубже у нас иерархия компонентов – тем хуже.

VirtualDOM

если не используется shouldComponentUpdate или PureComponent - render будет вызван в любом случае, построится виртуальный DOM и если в верстке будет что-то изменено - вызовется перестроение реального DOM.

Для полноценной работы обычно подключают Redux.js - библиотека которая отвечает за слой Store архитектуры Flux. Для этого необходимо подключить библиотеку react-redux.js (см.React-Redux).

Объединение логики и представления это очень плохая идея. Поэтому компоненты в React обычно разделяют на Компонент-контейнер (умный) и Компонент-представления(глупый). Для лучшего понимания можно привести аналогию с паттерном MVC (Model - Controller - View). Компонент-представления это View - глуппый он потому, что не предстваляет откуда берутся данные. Компонент-контейнер это Controller.

Задачи Компонент-представления Компонент-контейнер
Цель как компонент выглядит(разметка, стили) Как это должно работать(получение данных, обновление)
Знает от Redux Нет Да
Считывание данных props подписан на Redux Store
Изменения данных через callback отправляет dispatch

Stateless-компоненты

В большинстве случаях рекомендуются использовать stateless-компоненты, это компонент который не имеет своего состояния this.state.

Нельзя использовать артибут ref с компонентом, постоенном на функции, т.к. функция не имеет экземпляра. Но можно использовать атрибут внутри этого компонента.

function CustomTextInput(props){
 //textInput задекларирован здесь, т.к. обратный вызов ref ссылается на него
 let textInput = null;

 function handleClick(){
  textInput.focus();
 }

 return(
  <div>
   <input
    type="text"
    ref={{(input) => inputText=input}} />
    <input
     type="button"
     value="Focus the text input"
     onClick={handleClick}/>
  </div>
 );
}

React-Route

React-route - это маршрутизатор между разными url-ами.

ReactDOM.render({
 <Router>
  <Route path='/' component={Home} />
 </Router>,
 document.getElementById('root')
});

Route и Router это компоненты входят в библиотеку React-Route, они сами не отрисовывают DOM, а только реализуют маршрутизацию.

Расмотрим пример с вложенными маршрутами:

<Router>
 <Route component={MainLayout}>
  <Route component={SearchLayout}>
   <Route path='user' component={UserList}/>
   <Route path='widgets' component={WidgetList}/>
  </Route>
 </Route>
</Router>

Дополнительные атрибуты маршрута

<Router>
  <Route path="/" component={App}>
    <IndexRoute component={Home}/>
    <Route path="accounts" component={Accounts}/>
    <Route path="statements" component={Statements}/>
  </Route>
</Router>

С помощью IndexRoute переходя по ссылке '/' будет render-ица App с компонентом Home, который будет доступен через this.props.children.

Если необходимо сделать ссылки на маршруты, необъодимо использовать компонент Link, пример:

<ul>
 <li><Link to='/'>Home</Link></li>
 <li><Link to='/users'>Users</Link></li>
 <li><Link to='/widgets>Widgets</Link></li>
</ul>

Link to='/' className='btn' в конечном итоге будет преобразован в a href='/' class='btn', сам компонент Link необходит для магии React-route

История браузера
Есть два подход остлеживания истории браузера:

  1. browserHistory
  2. hashHistory
    Основное различие что при hashHistory нужно использовать доп.символ в url: exapmlpe.com/#/users, а при browserHistory url пишется как обычный.

Есть один нюанс при работе с browserHistrory, если пользователь начинает работать с example.com и потом переходит на ссылку example.com/users - все работает нормально. Но если пользователь начинает работу сразу с example.com/users - тогда мы получим ошибку 404, как на рисунке:

Чтобы решить эту проблему нужно настроить маршрутизацию на сервере решение

Two-way Binding Helper

В React данные движутся в одном направлении, от родителя к детям. Но иногда нам необходимо сразу узнать об изменениях, например когда мы используем form - для введения каких либо данных (например фильтр поиска или расширенный поиск). Для реализации two-way binding можно использовать LinkedStateMixin - это обертка надо setState и onChange. Давайте расмотрим простой пример:

const NoLink = React.createClass{
 getInitialState: function(){
  return {message: 'Hello'}
 },

 handleChange: function(event){
  this.setState({
   message: event.target.value
  });
 },

 render: function(){
  const {message} = this.state;
  return <input type='text' value={massage} onChange={this.handleChange} />;
 }
}

Это стандартная реализация компонента с полем ввода. Но с помощью LinkedStateMixin это можно сделать еще проше.

const WithLink = React.createClass{
 //сначало подключим миксин
 mixins: [LinkedStateMixin],

  getInitialState: function(){
   return {massage: 'Hello'};
  },

 render: function(){
  const {message} = this.state
  return <input type='text' valueLink={this.linkState('message')}
 }
}

LinkedStateMixin добавляет метод linkState - синтаксический сахар. Также необходимо использовать valueLink за место value в тегах, а для тега checkbox - checkedkLink. Давайте расмотрим как реализуется valueLink, если бы мы его использовали без mixin-а:

var WithoutMixin = React.creatClass{
 getInitialState: function(){
  return {message: 'Hello'};
 },

 handleChange: function(event){
  this.setState({message: event.target.value});
 },

 render: function(){
  const valueLink = {
   value: this.state.message,
   requestChange: this.handleChange
  };
  return <input type='text' valueLink={valueLink}/>
 }
}

На самом деле valueLink - это простой объект, который содержит value и requestChange

А теперь давайте расмотрим пример с использованием mixin, но без valueLink:

const WithoutValueLink = React.createClass{
 //сначало подключим миксин
 mixins: [LinkedStateMixin],

  getInitialState: function(){
   return {massage: 'Hello'};
  },

 render: function(){
  const valueLink = this.linkState('message');
  const handleChange = function(event){
   valueLink.requestchange(event.target.value);
  };
  return <input type='text' value={valueLink.value) onChange={handleChange}}
 }

Оптимизация

React медленный, React быстрый: оптимизация React-приложения на практике

Замичания

Очень плохая практика писать след код:

 return(
 <MyComponent
  onChange={this.handleChange.bind(this)} // т.к. при каждом рендере будет создаваться новая функция
  onClick={() => {this.handleClick()}} // тоже самое что и в пред примере
  style={{color: "green"}} // и тут тоже самое
  />
  );

Angular vs React

видео

results matching ""

    No results matching ""