Реагировать состояние инициализации компонента из реквизита


205

В React, есть ли реальные различия между этими двумя реализациями? Некоторые друзья говорят мне, что FirstComponent - это шаблон, но я не понимаю, почему. SecondComponent кажется проще, потому что рендеринг вызывается только один раз.

Первый:

import React, { PropTypes } from 'react'

class FirstComponent extends React.Component {

  state = {
    description: ''
  }

  componentDidMount() {
    const { description} = this.props;
    this.setState({ description });
  }

  render () {
    const {state: { description }} = this;    
    return (
      <input type="text" value={description} /> 
    );
  }
}

export default FirstComponent;

Во-вторых:

import React, { PropTypes } from 'react'

class SecondComponent extends React.Component {

  state = {
    description: ''
  }

  constructor (props) => {
    const { description } = props;
    this.state = {description};
  }

  render () {
    const {state: { description }} = this;    
    return (
      <input type="text" value={description} />   
    );
  }
}

export default SecondComponent;

Обновление: я изменил setState () на this.state = {} (спасибо joews), однако я до сих пор не вижу разницы. Один лучше других?


10
Почему вы храните свой реквизит в штате? Вместо этого вы должны использовать реквизиты напрямую, а не кэшировать значение. Прочитайте, почему Установка реквизитов в качестве состояния в React.js - это богохульство, а реквизиты в getInitialState - это анти-паттерн .
Aurora0001

12
Пример - переключаемый компонент (например, поповер или ящик). Родитель знает, должен ли компонент открываться или закрываться; сам компонент может знать, открыт он или нет в определенный момент времени. В этом случае я думаю, что this.state = { isVisible: props.isVisible }имеет смысл. Зависит от того, как приложение распространяет состояние пользовательского интерфейса.
16:00

2
Вы должны прочитать эту medium.com/@justintulk/...
FDisk

5
В 2017 году Facebook демонстрирует использование реквизита для установки начального состояния в своей документации: reactjs.org/docs/react-component.html#constructor
Rohmer

1
@ Aurora0001 Как в ситуации, когда вам нужно обрабатывать форму, скажем, форму редактирования, которая сама будет выполнять сетевые запросы, но вам нужно инициализировать входные данные значениями, которые будут использоваться в качестве поддержки этого компонента. Чтобы поддерживать динамическую форму, эти значения должны быть в состоянии.
Эрик Маквиннер,

Ответы:


196

Следует отметить, что копирование свойств, которые никогда не изменяются в состояние, является антипаттерном (в этом случае просто обращайтесь к .props напрямую). Если у вас есть переменная состояния, которая со временем изменится, но будет начинаться со значения из .props, вам даже не нужен вызов конструктора - эти локальные переменные инициализируются после вызова конструктора родителя:

class FirstComponent extends React.Component {
  state = {
    x: this.props.initialX,
    // You can even call functions and class methods:
    y: this.someMethod(this.props.initialY),
  };
}

Это стенографический эквивалент ответа из @joews ниже. Кажется, он работает только на более поздних версиях транспортеров es6, у меня были проблемы с ним при некоторых настройках веб-пакетов. Если это не работает для вас, вы можете попробовать добавить плагин babel babel-plugin-transform-class-properties, или вы можете использовать не сокращенную версию, используя @joews ниже.


1
Можете ли вы объяснить больше, чем ваш ответ отличается от ответа @joews?
Джалал

3
Добавлено «Вы можете пропустить вызов конструктора, если все, что вы делаете, это устанавливаете переменные».
Зейн Хупер

3
Если это не работает, вам, вероятно, нужно установить этот плагин babel "babel-plugin-transform-class-properties".
Фахим

2
Это не анти-паттерн для инициализации состояния из реквизита, если он понимает, что состояние не зависит от реквизита после инициализации. Если вы пытаетесь сохранить два синхронно, это анти-паттерн.
Yatrix

1
@ ak85 это тот же синтаксис, но вы бы использовали this.state. Этот синтаксис является всего лишь сокращенным синтаксисом для установки состояния во время процесса создания класса (и может также использоваться для переменных, отличных от состояния)
Zane Hooper

137

Вам не нужно вызывать setStateкомпоненты constructor- это идиоматично для this.stateпрямой установки :

class FirstComponent extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      x: props.initialX
    };
  }
  // ...
}

См. React docs - Добавление локального состояния в класс .

Нет преимущества для первого метода, который вы описываете. Это приведет ко второму обновлению непосредственно перед монтированием компонента в первый раз.


4
Хороший ответ. Возможно, стоит отметить, что это только для установки начального состояния; Вам все еще нужно использовать, setStateесли вы изменяете его в любой другой точке, иначе изменения могут не отображаться.
Аврора0001

Еще раз спасибо jowes, вторая документация facebook.github.io/react/docs/…
Леви Морейра

(извините, я нажимаю enter ..) мы должны использовать getInitialState для установки состояния реквизита, в более сложных задачах, если это просто, мы можем просто использовать this.props в рендере, правильно?
Леви Морейра

1
На заметку на полях: используйте super(props)в конструкторе. Обсуждение SO
cutemachine

2
Предложение joews работает в большинстве случаев, но будьте осторожны при отправке реквизитов непосредственно в this.state. Копирование реквизитов в this.state фактически является единственным источником правды ( medium.com/react-ecosystem/… ). Также Дан Абрамов однажды предложил не хранить значения реквизита в государстве. ( twitter.com/dan_abramov/status/749710501916139520/photo/1 ).
Хироки

33

Введено обновление для React 16.3 alpha static getDerivedStateFromProps(nextProps, prevState)( документы ) в качестве замены componentWillReceiveProps.

getDerivedStateFromProps вызывается после создания экземпляра компонента, а также при получении новых реквизитов. Он должен возвращать объект для обновления состояния, или нуль, чтобы указать, что новые реквизиты не требуют никаких обновлений состояния.

Обратите внимание, что если родительский компонент вызывает повторную визуализацию вашего компонента, этот метод будет вызываться, даже если реквизиты не изменились. Вы можете сравнить новые и предыдущие значения, если хотите обрабатывать только изменения.

https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops

Он статичен, поэтому не имеет прямого доступа к нему this(однако у него есть доступ prevState, который может хранить вещи, к которым обычно прикреплены, thisнапример refs)

отредактировано, чтобы отразить исправление @ nerfologist в комментариях


3
Просто чтобы прояснить, он называется getDerivedStateFromProps(отметьте заглавную букву в реквизите) и PARAMS является nextProps, prevState(не nextState): reactjs.org/docs/...
nerfologist

1
Вот Это Да! мы можем использовать это для обновления состояния при получении обновленных реквизитов !
Аромал Сасидхаран

2
Нужно ли нам создавать начальное состояние в конструкторе, учитывая, что getDerivedStateFromPropsон всегда вызывается перед начальным рендерингом?
bvdb

19

Вы можете использовать краткую форму, как показано ниже, если вы хотите добавить все реквизиты в состояние и сохранить те же имена.

constructor(props) {
    super(props);
    this.state = {
       ...props
    }
    //...
}

1
это анти-шаблон для копирования свойств, которые никогда не изменяются в состояние. Лучше явно описать, какие поля использует ваш компонент.
Майкл

5

установить данные о состоянии внутри конструктора, как это

constructor(props) {
    super(props);
    this.state = {
      productdatail: this.props.productdetailProps
    };
  }

это не сработает, если вы установите в методе componentDidMount () стороны через props.


3

Если вы напрямую инициируете состояние из реквизита, в React 16.5 (5 сентября 2018 г.) будет показано предупреждение.


Любая идея, почему он будет предупреждать?
Нитин Джадхав

2
Похоже, это только если вы используете state = props. Более подробная информация здесь: github.com/facebook/react/pull/11658#issuecomment-419677176
теперь

2

Вы можете использовать keyзначение для сброса состояния, когда это необходимо, передать реквизиты, чтобы указать, что это не очень хорошая практика, потому что у вас есть неконтролируемый и контролируемый компонент в одном месте. Данные должны обрабатываться в одном месте, прочитайте это https://reactjs.org/blog/2018/06/07/you-probbly-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a -key


1

Вы можете использовать componentWillReceiveProps.

constructor(props) {
    super(props);
    this.state = {
      productdatail: ''
    };
  }

    componentWillReceiveProps(nextProps){
        this.setState({ productdatail: nextProps.productdetailProps })
    }

1
componentWillReceiveProps устарел, не может использоваться для будущих версий
Вивек Ганчи

1

ВЫ должны быть осторожны, когда вы инициализируете stateиз propsконструктора. Даже если оно будет propsизменено на новое, состояние не изменится, потому что mount никогда не повторится. Так getDerivedStateFromPropsсуществует для этого.

class FirstComponent extends React.Component {
    state = {
        description: ""
    };

    static getDerivedStateFromProps(nextProps, prevState) {
        if (prevState.description !== nextProps.description) {
          return { description: nextProps.description };
        }

        return null;
    }

    render() {
        const {state: {description}} = this;    

        return (
            <input type="text" value={description} /> 
        );
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.