Гайд как писать на React

Гайд как писать на React

Coding

Class Components

Компоненты на основе классов — это stateful компоненты (с внутренним состоянием). Использовать их надо только там где они нужны!

Давайте напишем один из них

Импортируем зависимости

import React, { Component } from 'react'
import styles from './styles.css'


Отделяйте локально импортируемые зависимости от других. Повышает читаемость кода.

Инициализация state

import React, { Component } from 'react'
import styles from './styles.css'
export default class Container extends Component {
  state = { name: 'Input' }


state уже можно объявлять без конструктора.

Он обычно используется только в трёх случаях:

  1. Вам нужны props для того, чтобы поместить их в state.
  2. Вам нравится старый подход ??
  3. Вам нужно использовать debounce или throttle для методов.

propTypes и defaultProps

import React, { Component } from 'react'
import { string } from 'prop-types'
import styles from './styles.css'
export default class Container extends Component {
  static propTypes = {
    title: string
  }
 
  static defaultProps = {
    title: 'React'
  }
  state = { name: 'Input' }


propTypes и defaultProps это статичные свойства. Объявлять их надо как можно выше (позже вам скажут спасибо многие разработчики).

Кстати, если вы используйте React 15.3.0 и выше, то теперь PropTypes это отдельная библиотека prop-types.

Методы

import React, { Component } from 'react'
import { string } from 'prop-types'
import Input from '../Input'
import styles from './styles.css'
export default class Container extends Component {
  static propTypes = {
    title: string
  }
 
  static defaultProps = {
    title: 'React'
  }
  state = { name: 'Input' }
  onInputChange = (e) => this.setState({ name: e.target.value })


Если ваш метод попадёт в children, то надо убедиться, что this будет указывать на компонент родителя.

Раньше это делали так

this.onInputChange.bind(this) // старый подход

Но с приходом ES6 появились arrow function () => {} — этот подход более чистый и компактный, используйте его.

Деструктуризация Props

import React, { Component } from 'react'
import { string } from 'prop-types'
import Input from '../Input'
import styles from './styles.css'
export default class Container extends Component {
  static propTypes = {
    title: string
  }
 
  static defaultProps = {
    title: 'React'
  }
  state = { name: 'Input' }
  onInputChange = (e) => this.setState({ name: e.target.value })
  render() {
    const { title } = this.props;
    return (
      <Input
        onChange={this.onInputChange}
        title={title}
      />
    )
  }

С помощью деструктуризации теперь можно писать вот так

const {
  name,
  title
} = this.props

Вместо

const title = this.props.title
const name = this.props.name


Каждый props который приходит в компоненту должен быть описан с новой линии. Повышает читаемость.

Декораторы

Декораторы пока не являются стандартом и возможно будут переделаны в будущем, используйте на свой страх и риск (он минимален)
import React, { Component } from 'react'
import { string } from 'prop-types'
import { connect } from 'react-redux'
import Input from '../Input'
import styles from './styles.css'
const mapStateToProps = state => ({ title: state.title })
@connect(mapStateToProps)
export default class Container extends Component {
  static propTypes = {
    title: string
  }
 
  static defaultProps = {
    title: 'React'
  }
  state = { name: 'Input' }
  onInputChange = (e) => this.setState({ name: e.target.value })
  render() {
    const {
      title
    } = this.props;
    return (
      <Input
        onChange={this.onInputChange}
        title={title}
      />
    )
  }

Если вы используйте много классов обёрток(HOC) или такие библиотеки как redux, то с появлением декораторов это стало намного удобнее.

Без них мы бы до сих пор делали это так

class Container extends Component {
  ...
}
export default connect(mapStateToProps)(Container);


Лучше всего выносить логику с redux в отдельный компонент, который будет контейнером для текущего.

- Profile
--- index.js
--- ProfileContainer.js <-- логика с redux
--- Profile.js <-- ваш компонент





Functional Components

У этих компонентов нету состояния (stateless). Они чисты и понятны, используйте их как можно чаще.

Чем они отличаются от statefull компонентов ?

propTypes

import React from 'react'
import { string } from 'prop-types'
import style from './styles.css'
Stateless.propTypes = {
  title: string
}
// Component declaration


Объявление proptypes вынесены из компонента.

Деструктуризация Props и defaultProps

import React from 'react'
import { string } from 'prop-types'
import style from './styles.css'
Stateless.propTypes = {
  title: string
}
function Stateless(props) {
  const { title } = props;
  return (
    <div>{title || 'React'}</title>
  )
}

Теперь наш компонент это функция и мы можем делать так

import React from 'react'
import { string } from 'prop-types'
import style from './styles.css'
Stateless.propTypes = {
  title: string
}
// defalutProps можно объявлять в самих props - title = 'React'
function Stateless({ title = 'React' }) {
  return ( <div>{title}</div> )
}

Выглядит классно, но можно ещё лучше

import React from 'react'
import { string } from 'prop-types'
import style from './styles.css'
Stateless.propTypes = {
  title: string
}
const Stateless = ({ title = 'React' }) => <div>{title}</div>


Некоторые разработчики не советуют так делать, наша функция unnamed и при возникновении ошибки вы увидите в консоле <<anonymous>>.


Но это не проблема если ваш Babel настроен правильно.

Как улучшить код дальше ?


HOC

Многие привыкли делать запросы на сервер в методе componentDidMount , но с появлением такого подхода как Higher-Order Components стоит вынести их повыше.

К примеру у нас есть компонент Profile и для его отображения ему нужен объект user приходящий с сервера.

Создадим HOC который будет принимать пользователя и передавать его в Profile.

// HOC withUser
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser } from 'actions';
const mapDispatchToProps = {
  fetchUser
};
const withUser = (WrapedComponent) => {
  @connect(null, mapDispatchToProps)
  class AsyncComponent extends Component {
    componentWillMount = async () => {
      const user = await fetchUser(); // наш запрос
      this.setState({ user });
    }
    render() {
      const { user } = this.state;
      const newProps = { user };
      if (!user) return null; // если user ещё нет вернём null
      return (
        <WrapedComponent {...this.props} {...newProps} />
      )
    }
  }
  return AsyncComponent;
}
export default withUser;


Многим код покажется сложноват, но поверьте, тут всё просто. withUser — это обёртка над нашим компонентом, которая не даст ему отрендериться пока не придёт user.

Как и с чем использовать ?

Если вашей компоненте нужен user, то просто добавим наш HOC как декоратор

import withUser from 'decorators/withuser';
@withUser
class Profile extends Component {
  ...
  render() {
    const { user } = this.props;
    return ( <div>{user.name}</div> )
  }
}

Также с помощью декораторов и HOC можно делать интересные вещи. К примеру давайте отображать загрузочный экран пока user идёт к нам с сервера.

// Loading
const Loading = (component) => component ?
  component : <div>Loading...</div>

// Profile
import withUser from 'decorators/withuser'
import Loading from 'components/Loading'
import { compose } from 'redux'
class Profile extends Component {
  ...
}
export default compose(
  withUser,
  Loading
)(Profile)

Reselect

Библиотека которая не даст компоненту перерисовываться, если входные данные (props) не изменились.

Recompose

Это как lodash только для реакта.

Redux-actions

Уменьшит код в 2 раза. Повысит читаемость.

Пример

До

export function increment(counter) {
  return {
    type: INCREMENT,
    payload: {
      counter
    }
  }
}

После

const increment = createAction(INCREMENT);



Не забывайте ставить 👍 если вам понравилась статья и подписаться на канал


Report Page