Страница не обновляется для отражения новых данных из веб-сокета с Redux-Saga и React - PullRequest
0 голосов
/ 04 мая 2020
• 1000 * Вот ссылка на видео о том, что я описываю: https://drive.google.com/file/d/1qTKTdItvK4jhdHQUZ19rPdWUSRmc8r2E/view?usp=sharing

Процесс должен быть:

  1. Я вношу изменения и отправляю изменение веб-сокета
  2. веб-сокет генерирует данные, которые правильно представляют новые данные
  3. состояние обновления страницы для отражения новых данных

однако шаг 3 никогда не обновляет штат. Мне нужно принудительно выполнить обновление, щелкнув в другом месте, чтобы данные появились, поэтому я знаю, что данные есть. Кроме того, по какой-то причине ни componentWillReceiveProps, ни getDerivedStateFromProps, похоже, не срабатывают.

Вот часть моего кода:

Мой компонент

class RiskSettings extends PureComponent {
  constructor(props) {
    super(props)

    this.state = {
      risk_setting: this.props.risk_setting,
      showConfigureDisplayModal: false,
      displayOptions: ['firm', 'trader', 'marginlimit'],
      maxAmountOfOptions: 10,
      entryToEdit: {
        item: '',
        option: '',
        value: ''
      }
    }
  }

  // componentWillReceiveProps(nextProps) {
  //   console.log('test')
  //   if (nextProps !== this.props) {
  //     this.setState({
  //       risk_setting: nextProps.risk_setting
  //     })
  //   }
  // }

  // static getDerivedStateFromProps(nextProps, prevState) {
  //   if (nextProps.risk_setting !== prevState.risk_setting) {
  //     return {
  //       risk_setting: nextProps.risk_setting
  //     }
  //   }
  //
  //   return null
  // }

  handleCloseConfigureDisplayModal = () => {
    this.setState({
      showConfigureDisplayModal: false
    })
  }

  handleShowConfigureDisplayModal = () => {
    this.setState({
      showConfigureDisplayModal: true
    })
  }

  parseKeysIntoArray = object => {
    if (Object.keys(object).length > 0) {
      return Object.keys(object)
    } else {
      return []
    }
  }

  handleModifyDisplayOptions = key => {
    const newOptions = this.state.displayOptions

    if (newOptions.includes(key)) {
      const index = newOptions.indexOf(key)
      newOptions.splice(index, 1)
    } else {
      if (newOptions.length < this.state.maxAmountOfOptions) {
        newOptions.push(key)
      }
    }

    this.forceUpdate()
  }

  handleResetDisplayOptions = () => {
    this.setState({
      displayOptions: ['firm', 'trader', 'marginlimit'],
    })
  }

  handleSelectEntryToEdit = (option, item, value) => {
    this.setState({
      entryToEdit: {
        option: option,
        item: item,
        value: value,
      }
    })
  }

  handleStopEditingEntry = () => {
    this.setState({
      entryToEdit: {
        option: '',
        item: '',
        value: ''
      }
    })
  }

  handleEditEntry = (e, item, option) => {
    this.setState({
      entryToEdit: {
        ...this.state.entryToEdit,
        value: e.target.value
      }
    })
  }

  handleSaveEditedEntry = () => {
    const { option, value, item } = this.state.entryToEdit
    const new_risk_setting = this.state.risk_setting

    const message = {
      type: 'account_update',
      firm: item.firm,
      trader: item.trader,
    }

    // new_risk_setting[item.trader][option] = value

    message[option] = value

    this.props.websocket.send(JSON.stringify(message))
    this.handleStopEditingEntry()
  }

  /****************************************************************************/

  renderLoading = () => (
    <Row noGutters className='app-spinner'>
      <Spinner animation='border' className='common-grey-spinner' />
    </Row>
  )

  renderTable = props => {
    const { displayOptions } = this.state
    const risk_setting = this.props.risk_setting
    const arrayOfKeys = this.parseKeysIntoArray(risk_setting)

    let body
    if (arrayOfKeys.length > 0) {
      const arrayOfValues = Object.values(risk_setting)
      body = arrayOfValues.map((item, index) => this.renderTableItem(item, index))
    } else {
      body = this.renderLoading()
    }

    let tableHeaders
    if (displayOptions.length > 0) {
      tableHeaders = displayOptions.map((option, index) => this.renderTableHeader(option, index))
    }

    return (
      <Table responsive bordered className='submissions-table'>
        <thead>
          <tr>
            {tableHeaders}
          </tr>
        </thead>

        <tbody>
          {body}
        </tbody>
      </Table>
    )
  }

  renderTableHeader = (option, index) => (
    <th key={'tableHeader - index: ' + index} className='table-header-cell'>{option}</th>
  )

  renderTableItem = (item, index) => {
    const { displayOptions } = this.state

    let tableContents
    if (displayOptions.length > 0) {
      tableContents = displayOptions.map((option, index) => this.renderMatchingTableItem(item, option, index))
    }
    return (
      <tr key={'tableItem - index: ' + index} className='table-cell'>
        {tableContents}
      </tr>
    )
  }

  renderMatchingTableItem = (item, option, index) => {
    const { entryToEdit } = this.state

    let content
    if (option == 'trader' || option == 'firm') {
      content = item[option]
    } else {
      if (entryToEdit.item.trader === item.trader && entryToEdit.option === option) {
        content = (
          <Row noGutters className='table-cell-row'>
            <FormControl
              onChange={e => this.handleEditEntry(e, item, option)}
              value={entryToEdit.value}
              className='table-cell-formControl'/>
            <div>
              <AiOutlineClose
                onClick={this.handleStopEditingEntry}
                className='individual-form-icon' />
              <AiOutlineSave
                onClick={() => this.handleSaveEditedEntry()}
                className='individual-form-icon' />
            </div>
          </Row>
        )
      } else {
        content = (
          <Row noGutters className='table-cell-row'>
            {item[option]}
            <AiOutlineEdit
              className='individual-form-icon'
              onClick={() => this.handleSelectEntryToEdit(option, item, item[option])} />
          </Row>
        )
      }
    }


    return (
      <td
        key={'matchingTableItem - index: ' + index}
        className='matching-table-item'>
        {content}
      </td>
    )
  }

  renderConfigureDisplayButton = () => {
    return (
      <Button
        onClick={this.handleShowConfigureDisplayModal}
        className='teal-button' size='sm'>
        Configure Display
      </Button>
    )
  }

  renderConfigureDisplayModal = () => {
    const { showConfigureDisplayModal, risk_setting, maxAmountOfOptions } = this.state
    const arrayOfKeys = this.parseKeysIntoArray(risk_setting)

    let content
    if (arrayOfKeys.length > 0) {
      const values = risk_setting[arrayOfKeys[0]]
      const arrayOfKeysFromValues = Object.keys(values)
      content = arrayOfKeysFromValues.map(key => this.renderKeyCheckbox(key))
    }

    return (
      <Modal
        centered
        className='common-modal'
        show={showConfigureDisplayModal}
        onHide={this.handleCloseConfigureDisplayModal}>

        <Modal.Header className='common-modal-header' closeButton>
          Risk Settings Display Options
        </Modal.Header>

        <Modal.Body>
          <div className='max-amount-of-options'>
            You can select up to {maxAmountOfOptions} options
          </div>
          <Form>
            <Row noGutters className='risk-settings-content-row'>
              {content}
            </Row>
          </Form>
        </Modal.Body>

        <Modal.Footer>
          <AiOutlineRedo
            onClick={this.handleResetDisplayOptions}
            className='individual-form-icon' />
          <Button
            onClick={this.handleCloseConfigureDisplayModal}
            className='teal-button' size='sm'>
            Save
          </Button>
        </Modal.Footer>

      </Modal>
    )
  }

  renderKeyCheckbox = key => {
    const { displayOptions } = this.state
    return (
      <Form.Check
        checked={displayOptions.includes(key) ? true : false}
        onChange={() => this.handleModifyDisplayOptions(key)}
        className='risk-settings-key-checkbox'
        key={key} name={key} size='sm' label={key} />
    )
  }

  render() {
    return (
      <Container fluid className='forms-container'>
        {this.renderConfigureDisplayModal()}

        <Row noGutters className='forms-header'>
          <h4>Risk Settings</h4>
          {this.renderConfigureDisplayButton()}
        </Row>

        <div className='forms-table-box'>
          {this.renderTable()}
        </div>

      </Container>
    )
  }
}

export default RiskSettings

Мой eventChannel

    function createEventChannel(mySocket, userData) {
  return eventChannel(emitter => {

    mySocket.onopen = () => {
      console.log('opening connection...')
      const challenge = JSON.stringify({ type: 'challenge' })
      mySocket.send(challenge)
    }

    mySocket.onmessage = e => {
      const message = JSON.parse(e.data) || ''

      if (message) {
        // console.log('message: ', message)
        if (message.type === 'challenge' && message.result === 'OK') {

          const encrypt = new JSEncrypt()
          encrypt.setPublicKey(message.key)
          const encryptedPass = encrypt.encrypt(userData.pass)

          const loginMessage = JSON.stringify({
            type: 'login',
            userid: userData.userid,
            pass: encryptedPass
          })
          mySocket.send(loginMessage)

        } else if (message.type === 'current_risk') {

        } else if (message.type === 'account_update') {

          // if (message.result === 'OK') {
          //   // console.log(message)
          //   return emitter({ type: 'INJECT_TRADER', message })
          // } else {
          //   toast.error(message.result)
          // }

        } else if (message.type === 'risk_setting') {

          return emitter({ type: 'INJECT_TRADER', message })

        } else if (message.type === 'login' && message.result === 'OK') {

          return emitter({ type: 'LOGIN_SUCCESS', userData })

        } else if (message.type === 'login' && message.result !== 'OK') {

          toast.error(message.result)
          return emitter({ type: 'LOGIN_ERROR' })
        }
      }
    }

    return () => {
      mySocket.close()
      console.log('socket off')
    }
  })
}

Мой редуктор

import { history } from '../../configureStore';
import * as CONSTANTS from '../constants/admin';

const initialState = {
  risk_setting: {},
}

export default (state = initialState, action) => {
  switch (action.type) {
    case CONSTANTS.INJECT_TRADER:
    // console.log(action.message)
      const new_risk_setting = state.risk_setting
      new_risk_setting[action.message.trader] = action.message
      return {
        ...state,
        risk_setting: new_risk_setting
      }
    // case CONSTANTS.UPDATE_RISK_SETTINGS_FIELD:
    //   const updated_risk_settings = state.risk_setting
    //   update_risk_settings[action.data.trader] = action.data
    //   return {
    //     ...state,
    //     risk_setting: new_risk_setting
    //   }
    default:
      return state
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...