简体   繁体   English

回调内部的setState,以在React组件中呈现相关内容

[英]setState from inside of a callback to render related content in a React component

I'm stuck with conditional content rendering, depending on a value presence. 我坚持使用条件内容渲染,具体取决于值的存在。 In the component, if API key is not given, I want to show an input with a button. 在组件中,如果未提供API密钥,我想显示带有按钮的输入。 When the API key is stored using electron-json-storage and as a state, the component renders another html. 当使用electron-json-storage并以状态electron-json-storage API密钥时,该组件将呈现另一个html。

My problem is I cannot assign the state during component's construction. 我的问题是在构造组件期间无法分配状态。

// @flow
import React, { Component } from 'react';
import storage from 'electron-json-storage';
import {
        Button,
        Popover,
        PopoverInteractionKind,
        Position
       } from '@blueprintjs/core';

export default class UserButton extends Component {
  constructor() {
    super();

  // -----------------------------------------------------------
  // HERE - I'm trying to assign keys from storage via callback
  // Unfortunately it doesn't work
  // It throws Cannot read property 'is_loggedin' of null
  // when renders content={loginContent[this.state.is_loggedin]}
  // -----------------------------------------------------------
  storage.get('auth', (error, data) => {
    if (error) throw error;
    this.state = {
      api_key: data.user.api_key,
      is_loggedin: data.user.is_loggedin
    };
  });

    this.handleTextChange = this.handleTextChange.bind(this);
    this.handleSaveClick = this.handleSaveClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
  }

  handleTextChange(e) {
    this.setState({ api_key: e.target.value });
  }

  handleSaveClick() {
    this.setState({ is_loggedin: 1 });

    storage.set('auth', { user: this.state }, function (error) {
      if (error) throw error;
    });

    this.forceUpdate();
  }

  handleLogoutClick() {
    this.setState({ is_loggedin: 0 });

    storage.remove('auth', function (error) {
      if (error) throw error;
    });

    this.forceUpdate();
  }

  render() {
    const loginContent = ([
      // Not logged in
      <div>
        <label className="pt-label .modifier">
          <input
            onChange={this.handleTextChange}
            autoFocus={true}
            className="pt-input modifier"
            type="text"
            placeholder="Your API key"
          />
        </label>

        <Button
          className="pt-intent-primary pt-fill"
          onClick={this.handleSaveClick}
        >
          Save
        </Button>
      </div>,
      // Logged in
      <div>
        <Button
          className="pt-intent-primary pt-fill"
          onClick={this.handleLogoutClick}
        >
          Change API key
        </Button>
      </div>
    ]);

    return (
      <Popover
        content={loginContent[this.state.is_loggedin]}
        interactionKind={PopoverInteractionKind.CLICK}
        position={Position.BOTTOM_RIGHT}
        popoverClassName="pt-popover-content-sizing"
      >
        <Button className="pt-button pt-minimal pt-icon-user" />
      </Popover>
    );
  }
}

How this could be done? 如何做到这一点?

You are probably rendering the component before the callback is called, and so state is still empty. 您可能在调用回调之前渲染了组件,因此state仍然为空。 What you could do is add a temporary, is_loading state in the constructor that renders a loading message/spinner in the page until the callback is called. 您可以做的是在构造函数中添加一个临时的is_loading状态,该状态会在页面中呈现加载消息/微调is_loading ,直到调用回调。

constructor() {
    super();

    this.state = { is_loading: true };

    storage.get('auth', (error, data) => {
        if (error) throw error;
        this.setState({
            is_loading: false,
            api_key: data.user.api_key,
            is_loggedin: data.user.is_loggedin
        });
    });

    this.handleTextChange = this.handleTextChange.bind(this);
    this.handleSaveClick = this.handleSaveClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
}

// ...

render() {
    // ...

    return (
        <Popover
            content={this.state.is_loading? "<div>Loading...</div>": loginContent[this.state.is_loggedin]}
            interactionKind={PopoverInteractionKind.CLICK}
            position={Position.BOTTOM_RIGHT}
            popoverClassName="pt-popover-content-sizing"
        >
            <Button className="pt-button pt-minimal pt-icon-user" />
        </Popover>
    );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM