Установить начальное состояние для диалогового окна материала - PullRequest
2 голосов
/ 03 октября 2019

У меня есть MaterialUI диалог , в котором есть несколько текстовых полей, раскрывающихся списков и других вещей. Некоторые из этих элементов должны быть установлены на определенное значение при каждом открытии или повторном открытии диалога. Другие элементы не могут быть загружены, пока не будут выполнены определенные условия (например, загружены пользовательские данные).

Для «сброса» я использую функцию onEnter. Но функция onEnter не запускается до тех пор, пока не введет (дух!) ... но сама функция рендеринга все еще работает - это означает, что любая логика или доступ к переменным javascript в JSX все равно будут происходить. Это оставляет функцию «onEnter» плохо оборудованной, чтобы быть тем местом, которое я настроил и инициализировал мой диалог.

Я также не могу использовать конструктор для установки / сброса этого начального состояния, так как данные мне нужныКонструкция состояния может быть недоступна во время загрузки конструктора (после запуска приложения). Теперь я мог бы усложнить мой JSX в своей функции рендеринга и сделать условия для каждой точки данных ... но это много накладных расходов на то, что перерисовывается каждый раз, когда приложение что-то меняет. (Диалоговые окна пользовательского интерфейса материала запускают всю функцию визуализации, даже если для параметра 'open' установлено значение false).

Каков наилучший способ справиться с инициализацией значений для диалогового окна пользовательского интерфейса материала?

Здесь приведен пример с очень тупыми данными (в реальной жизни представьте, что getInitialState - гораздо более сложная, медленная и потенциально асинхронная / сетевая функция) - давайте представим, что пользовательский объект недоступен в приложении. в начале и на самом деле некоторые данные извлекаются или вводятся задолго до запуска приложения. Этот код завершается ошибкой, потому что «пользователь» не определен при первом рендеринге (что происходит ДО запуска onEnter).

constructor(props) {
    super(props);
}

getInitialState = () => {
    return {
        user: {username: "John Doe"}
    }
}

onEnter = () => {
    this.setState(this.getInitialState())
}


render() {
    const { dialogVisibility } = this.props;

    return (
        <Dialog  open={dialogVisibility}  onEnter={this.onEnter}>
            <DialogTitle>
                Hi, {this.state.user.username}
            </DialogTitle>
        </Dialog> );
}

Мой первый инстинкт был в том, чтобы поместить в состояние переменную «isInitialized» и позволить только рендерингуверните диалог, если «isInitialized» имеет значение true, например:

constructor(props) {
    super(props);
    this.state = {
        isInitialized: false
    };
}

getInitialState = () => {
    return {
        user: {username: "John Doe"}
    }
}

onEnter = () => {
    this.setState(this.getInitialState(), 
        () => this.setState({isInitialized:true})
    );
}


render() {
    const { dialogVisibility } = this.props;

    if(!this.state.isInitialized) {
        return null;
    }

    return (
        <Dialog  open={dialogVisibility}  onEnter={this.onEnter}>
            <DialogTitle>
                Hi, {this.state.user.username}
            </DialogTitle>
        </Dialog> );
}

Как я уверен, вы знаете ... это не сработало, так как мы никогда не возвращаем диалог, чтобы запуститьonEnter событие, которое, в свою очередь, запускает функцию onEnter и фактически инициализирует данные. Я попытался изменить условное выражение !this.state.inInitialized на это:

    if(!this.state.isInitialized) {
        this.onEnter();
        return null;
    }

, и это работает ... но это дает мне предупреждение во время выполнения: Warning: Cannot update during an existing state transition (such as within рендер ). Render methods should be a pure function of props and state.

ЭтоЯ много читал, в частности, этот вопрос: Вызов setState при рендеринге недопустим , что действительно привело к тому, что я не должен просто игнорировать это предупреждение. Кроме того, этот метод приводит к тому, что вся логика, содержащаяся в возвращаемом JSX, по-прежнему возникает ... даже когда диалог не "открыт". Добавьте несколько сложных диалогов, и это снизит производительность.

Конечно, есть «правильный» способ сделать это. Помогите? Мысли?

Ответы [ 3 ]

1 голос
/ 03 октября 2019

Вы должны использовать componentDidUpdate ()

  • Этот метод не вызывается для начального рендеринга
  • Используйте его как возможность работать с DOM, когдакомпонент обновлен

Если вам необходимо предварительно загрузить данные перед открытием диалогового окна, вы можете использовать componentDidMount () :

  • вызывается немедленнопосле монтирования компонента (вставки в дерево)
  • если вам нужно загрузить данные из удаленной конечной точки, это хорошее место для создания экземпляра сетевого запроса

Реагируйте, ребята, добавленыхук useEffect предназначен для случаев, подобных описываемому вами, но вам потребуется рефакторинг для функционального компонента. Источник: https://reactjs.org/docs/hooks-effect.html

1 голос
/ 03 октября 2019

Что вам нужно концептуально, так это то, что, когда вы только что открыли диалоговое окно, вы хотите сбросить некоторые элементы. Таким образом, вы хотите иметь возможность прослушивать, когда значение open изменяется с false на true.

Для крючков руководство по реагированию предоставляет пример для сохранения«старое» значение данного предмета с крючком usePrevious. Тогда это просто вопрос использования useEffect.

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

function MyDialog({ dialogVisibility }) {
  const prevVisibility = usePrevious(dialogVisibility);

  useEffect(() => {
    // If it is now open, but was previously not open
    if (dialogVisibility && !prevVisibility) {
      // Reset items here
    }
  }, [dialogVisibility, prevVisibility]);

  return <Dialog open={dialogVisibility}></Dialog>;
}

То же самое можно достичь с помощью классов, если вы используете componentDidUpdate и параметр previousProps, который он получает.

export class MyDialog extends Component {
  public componentDidUpdate({ dialogVisibility : prevVisibility }) {
    const { dialogVisibility } = this.props;

    if (dialogVisibility && !prevVisibility) {
      // Reset state here
    }
  }

  public render() {
    const { dialogVisibility } = this.props;

    return <Dialog open={dialogVisibility}></Dialog>;
  }
}
0 голосов
/ 03 октября 2019

Эту проблему можно решить, оставив функции constructor, getInitialState и onEnter как написанные, и добавив следующее троичное условие в функцию рендеринга:

render() {
    const { dialogVisibility } = this.props;

return (
    <Dialog  open={dialogVisibility}  onEnter={this.onEnter}>
        {this.state.isInitialized && dialogVisibility ? 
        <DialogTitle>
            Hi, {this.state.user.username}
        </DialogTitle> : 'Dialog Not Initialized'}
    </Dialog> );
)}

Это фактически позволяет диалогу использоватьэто соответственно «onEnter», получайте правильные переходы и избегайте запуска какой-либо расширенной сложной логики в JSX при рендеринге, когда он не виден. Это также не требует рефакторинга или дополнительной сложности программирования.

... Но, я признаю, это кажется супер "неправильным".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...