React更新组件但不更新实际的HTML怪异行为

时间:2018-03-07 12:17:43

标签: javascript reactjs

我正在经历一种非常奇怪的反应行为。这个组件有messageemailSubmitedText。在基于某些条件的渲染方法中,它应该呈现第一个或另一个。

现在起初它是message。我点击表格的提交。所有功能都发生了。

该组件重新渲染。在控制台日志中,我可以看到它应该呈现emailSubmitedText。在反应工具中它显示正确的文本。

然而,在实际的html和html检查器中,它仍然显示了普遍的文本。

import React, { Component } from 'react'
import PropTypes from 'prop-types'

import Details from './Details'

class DefaultMessage extends Component {
  inputRef = null

  renderEmailForm = () => {
    return (
      <form
        className='b2c_email-form input-field'
        onSubmit={e => {
          e.preventDefault()
          const { projectId, visitSessionId } = this.props
          this.setState({ email: this.inputRef.value })
          this.props.onSubmitEmail({
            email: this.inputRef.value,
            convertedPage: window.location.href, projectId, visitSessionId
          })
        }}
      >
        <div className="input-field">
          <input ref={elem => this.inputRef = elem} id='email' type='email' className='validate' value={this.props.email} />
          <label htmlFor='email' data-error='Invalid email address'>E-mail</label>
        </div>
        <button
          className='b2c_email-form-button waves-effect waves-light btn'
          type='submit'
          style={{
            backgroundColor: this.props.companyColor || '#63bc78'
          }}
        >Submit</button>
      </form>
    )
  }

  render = () => {
    console.log('...> ', this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText)
    return (
      <div className='b2c_chat-message'>
        <Details
          classNames='b2c_chat-message-details__admin'
          avatar={this.props.avatar}
          name={this.props.name}
          date={this.props.date}
        />
        <div className='b2c_chat-message-text b2c_chat-message-text__admin b2c_chat-message-default'>
          <div className='b2c_chat-message-after b2c_chat-message-after__admin' />
          {this.props.error || !this.props.contactId && this.props.message || this.props.emailSubmitedText}
          {!this.props.contactId && this.renderEmailForm()}
        </div>
      </div>
    )
  }
}

DefaultMessage.propTypes = {
  projectId: PropTypes.string.isRequired,
  visitSessionId: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  email: PropTypes.string.isRequired,
  date: PropTypes.string.isRequired,
  message: PropTypes.string.isRequired,
  onSubmitEmail: PropTypes.func.isRequired
}

export default DefaultMessage

这是组件的直接父级。

import React, { Component } from 'react'
import PropTypes from 'prop-types'

import $ from 'jquery'
import moment from 'moment'
import randomstring from 'randomstring'

import DefaultMessage from './DefaultMessage'
import Message from './Message'

import UserTypingIndicator from '../UserTypingIndicator'
import TypingIndicator from './TypingIndicator'

class Messages extends Component {
  chatRef = null

  componentDidUpdate () {
    this.scrollToTheLastMessage()
  }

  scrollToTheLastMessage = () => {
    const $chat = $(this.chatRef)
    const scrollTop = $chat.prop('scrollHeight') - $chat.innerHeight()
    $chat.scrollTop(scrollTop)
  }

  renderDefaultMessage = () => (
    <DefaultMessage
      contactId={this.props.contactId}
      companyColor={this.props.companyColor}
      error={this.props.error}
      date={moment().format('h:mm A')}
      name={this.props.adminName}
      avatar={this.props.adminAvatar}
      message={this.props.welcomeMessage}
      emailSubmitedText={this.props.emailSubmitedText}
      projectId={this.props.projectId}
      visitSessionId={this.props.visitSessionId}
      onSubmitEmail={this.props.onSubmitEmail}
    />
  )

  renderMessages = () => {
    let checkConversationDate = null
    const {messages, contactName, adminName, adminAvatar} = this.props
    const compareConversationDates = (createdAt) => {
      checkConversationDate = moment(createdAt).format("DD.MM.YYYY")
      return (
        <div key={randomstring.generate()} className='conversationDayDate'>
          <span>{checkConversationDate}</span>
        </div>
      )
    }
    if (!messages) return null
    return messages.map((message, index) => {
      return (
        <div>
          {checkConversationDate !== moment(message.createdAt.$date).format("DD.MM.YYYY") ? compareConversationDates(message.createdAt.$date)  : ''}
          {/* {index === 0 ? this.renderDefaultMessage() : ''} */}
          <Message
            isAdmin={message.userId ? true : false}
            imageFile={message.imageFile}
            key={randomstring.generate()}
            companyColor={this.props.companyColor}
            contactName={contactName}
            adminName={adminName}
            avatar={adminAvatar}
            message={message.message}
            date={moment(message.createdAt.$date).format('h:mm A')}
          />
        </div>
      )
    })
  }

  renderTypingIndicators = () => {
    const arrayToRender = [
      this.props.isAdminTyping && <AdminTypingIndicator />,
      this.props.isUserTyping && <UserTypingIndicator />
    ]
    return arrayToRender
  }

  render = () => <div ref={elem => this.chatRef = elem} id='chat' className='chat-body' style={{
    height: 'calc(100% - 190px - 3rem)',
    overflowY: 'scroll',
    margin: '30px 10px 10px 0',
    boxSizing: 'border-box'
  }}>
    {this.renderDefaultMessage()}
    {this.renderMessages()}
    {this.renderTypingIndicators()}
  </div>
}

Messages.propTypes = {
  projectId: PropTypes.string.isRequired,
  visitSessionId: PropTypes.string.isRequired,
  messages: PropTypes.array.isRequired,
  adminName: PropTypes.string.isRequired,
  contactName: PropTypes.string.isRequired,
  onSubmitEmail: PropTypes.func.isRequired
}

export default Messages

这里是带状态的容器

import React, { Component } from 'react'
import Sound from 'react-sound'
import ddp from '../../ddp'
import Cookies from 'js-cookie'
import randomstring from 'randomstring'

import ChatContainer from './ChatContainer'
import Icon from './Icon'

import { connect, makeArrayCollectionFromObjectCollection, getNewMessages } from '../../functions'

class View extends Component {
  defaultDocumentTitle = null

  state = {
    contactId: '',
    chat: null,
    show: false,
    newMessagesCount: null,
    notStatus: 'STOPPED'
  }

  newMessageNotification = newMessages => {
    if (newMessages.length && newMessages.length > this.state.newMessagesCount) {
      this.setState({ notStatus: 'PLAYING' })
      document.title = `(${newMessages.length}) ${this.defaultDocumentTitle}`
    } else if (!newMessages.length) {
      document.title = this.defaultDocumentTitle
    }
    if (this.state.newMessagesCount !== newMessages.length) {
      this.setState({ newMessagesCount: newMessages.length })
    }
  }

  componentWillMount () {
    this.defaultDocumentTitle = document.title
  }

  componentDidMount = async () => {
    this.setContactIdFromCookies()
    await connect(ddp)
  }

  setContactIdFromCookies = () => {
    window.Cookies = Cookies
    console.warn('setContactIdFromCookies')
    const contactId = Cookies.get('b2cContactId')
    console.log('contactId', contactId)
    if (contactId) this.setState({contactId})
  }

  componentDidUpdate () {
    console.warn('componentDidUpdate', this.props)
    if (this.state.contactId && !this.state.chat) {
      this.getChat(this.state.contactId)
    }
    if (this.state.chat && this.state.chat.length) {
      let newMessages = getNewMessages(this.state.chat)
      this.newMessageNotification(newMessages)
    }
  }

  componentWillReceiveProps = (nextProps) => {
    console.warn('componentWillReceiveProps', nextProps)
    if (!nextProps.contactId) return
    if (this.state.chat == null) this.getChat(nextProps.contactId)
  }

  getChat = async (contactId) => {
    console.log('getChat', contactId)
    await ddp.subscribe('Messages', {contactId})
    const messagesColl = ddp.getCollection('Messages')
    console.log('messagesColl', messagesColl)
    this.setState({chat: this.getMessages(messagesColl)})
    ddp.watch('Messages', (changedDoc, message) => {
      console.log('Messages collection item changed', changedDoc, message)
      const messagesColl = ddp.getCollection('Messages')
      this.setState({chat: this.getMessages(messagesColl)})
    })
  }

  getMessages = collection => {
    let messages = []
    if (collection) {
      messages = makeArrayCollectionFromObjectCollection(collection)
    }
    console.log('messages', messages)
    return messages
  }

  submitEmail = ({ email, convertedPage, projectId, visitSessionId }) => ddp.call('chat.init', { email, convertedPage, projectId, visitSessionId })
    .then(contactId => {
      Cookies.set('b2cContactId', contactId, { expires: 90 })
      this.setState({ contactId, error: '' })
    })
    .catch(error => {
      console.error('Error >', error)
    })

  readMessages = () => ddp.call('readMessages', {contactId: this.state.contactId, userId: !null})
    .then(res => {
      console.log('res', res)
    })
    .catch(error => {
      console.error('Error', error)
    })


  submitMessage = ({message, visitSessionId, imageFile}) => ddp.call('chat.submitContactMessage', { message, visitSessionId, contactId: this.state.contactId, projectId: this.props.projectId, imageFile: imageFile || null})
  .then((res) => {
    console.log('res', res)
  })
  .catch(error => {
    console.error('Error', error)
    this.setState({error})
  })

  toggleChat = () => this.setState((state) => ({show: !state.show}))

  sendFileToServer = (base64File, resolve, reject) => {
    ddp.call('uploadToDropbox', base64File)
    .then((res) => {
      this.submitMessage({message: '', visitSessionId: this.props.visitSessionId, imageFile: res})
      console.log('res', res)
    })
    .catch(error => {
      console.error('Error', error)
    })
  }

  getBase64 = (file, resolve, reject) => {
    const self = this
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = function () {
      self.sendFileToServer(reader.result, resolve, reject)
    }
    reader.onerror = function (error) {
      console.error('FileReader Error: ', error)
    }
  }


  onFileDrop = files => {
    let self = this
    files.forEach(file => {
      return new Promise((resolve, reject) => {
        self.getBase64(file, resolve, reject)
      })
    })
  }

  render () {
    return (
      <div>
        <ChatContainer
          onFileDrop={this.onFileDrop}
          contactId={this.state.contactId}
          show={this.state.show}
          error={this.state.error && <span style={{color: 'red'}}>{this.state.error}</span>}
          chatSettings={this.props.chatSettings}
          projectId={this.props.projectId}
          visitSessionId={this.props.visitSessionId}
          defaultAdminUserName='default defaultAdminUserName'
          contactName='You'
          supportName='Our Support'
          messages={this.state.chat}
          onSend={this.submitMessage}
          onSubmitEmail={this.submitEmail}
          toggleChat={this.toggleChat}
          readMessages={this.readMessages}
        />
        <Icon
          companyColor={this.props.chatSettings.companyColor}
          onClick={this.toggleChat}
          newMessagesCount={this.state.newMessagesCount}
        />
        <Sound
          url='https://www.incredo.co/hubfs/b2c/Not%201.wav'
          playStatus={this.state.notStatus}
          playFromPosition={0}
          onFinishedPlaying={() => this.setState({ notStatus: 'STOPPED' })}
        />
      </div>
    )
  }
}

export default View

0 个答案:

没有答案