Skip to content

Instantly share code, notes, and snippets.

@braska
Last active August 14, 2016 23:09
Show Gist options
  • Save braska/b84f74163df4610e7060db5eceb03573 to your computer and use it in GitHub Desktop.
Save braska/b84f74163df4610e7060db5eceb03573 to your computer and use it in GitHub Desktop.
Unexpected Remount React Component
import React from 'react';
import {Loader} from 'react-loaders';
import {Button} from 'react-bootstrap';
import DialogTopBar from '../components/DialogTopBar';
import DialogInputBarContainer from '../containers/DialogInputBarContainer';
import DialogMessagesLayoutContainer from '../containers/DialogMessagesLayoutContainer';
export default class DialogLayout extends React.Component {
static propTypes = {
currentDialog: React.PropTypes.object.isRequired,
fetchDialogInfo: React.PropTypes.func.isRequired,
params: React.PropTypes.object.isRequired,
online: React.PropTypes.bool.isRequired,
emit: React.PropTypes.func.isRequired,
closeDialog: React.PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
dialogHeight: 200,
messagesAreaBottomIndent: 0
};
}
componentWillMount() {
this.props.fetchDialogInfo(+this.props.params.id);
this.props.emit('subscribeToUserStatus', this.props.params.id, (isOnline) => {});
window.addEventListener('resize', this.calcDialogHeight);
}
componentWillUnmount() {
window.removeEventListener('resize', this.calcDialogHeight);
this.props.closeDialog();
}
componentWillUpdate(nextProps) {
if (nextProps.params.id !== this.props.params.id) {
this.props.fetchDialogInfo(+nextProps.params.id);
this.props.emit('subscribeToUserStatus', nextProps.params.id, (isOnline) => {});
}
}
calcDialogHeight = () => {
if (this._topbar) {
let height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - this._topbar.getClientRect().bottom - 10;
if (this.state.dialogHeight !== height) {
this.setState({dialogHeight: height});
}
}
};
updateMessagesAreaBottomIndent = () => {
if (this._inputBarContainer) {
let clientRects = this._inputBarContainer.getClientRects();
if (clientRects && clientRects[0]) {
let height = clientRects[0].height;
if (this.state.messagesAreaBottomIndent !== height) {
this.setState({
messagesAreaBottomIndent: this._inputBarContainer.getClientRects()[0].height
});
}
}
}
};
componentDidUpdate() {
this.updateMessagesAreaBottomIndent();
}
componentDidMount() {
this.updateMessagesAreaBottomIndent();
}
render() {
// ToDo: Сделать автоскрол зоны сообщений в самый низ при появлении нового сообщения, если в момент его появления скрол был в самом низу
return (
<div className="dialog-layout">
{(() => {
if (this.props.currentDialog.info.loading) {
return <Loader className="colored text-center" type="line-scale" />;
} else {
if (this.props.currentDialog.info.error) {
return (
<div className="text-center">
<p className="text-mutted">Произошла ошибка при загрузке диалога.</p>
<Button onClick={() => { this.props.fetchDialogInfo(this.props.params.id) }}>Повторить</Button>
</div>
);
} else {
return (
<div>
<DialogTopBar dialogInfo={this.props.currentDialog.info.data} online={this.props.online} ref={(c) => { if (c != null) { this._topbar = c; this.calcDialogHeight(); } }} />
<div style={{position: 'relative', height: this.state.dialogHeight + 'px'}}>
<div style={{paddingBottom: this.state.messagesAreaBottomIndent, height: '100%'}}>
<div style={{height: '100%', overflowY: 'scroll'}}>
<DialogMessagesLayoutContainer loading={this.props.currentDialog.messages.loading} error={this.props.currentDialog.messages.error} data={this.props.currentDialog.messages.data} opponent={this.props.currentDialog.info.data.opponent} />
</div>
</div>
<div style={{position: 'absolute', bottom: 0, left: 0, right: 0}} className="dialog-input-bar-container" ref={(c) => { if (c != null) { this._inputBarContainer = c; this.updateMessagesAreaBottomIndent(); } }}>
<DialogInputBarContainer opponent={this.props.currentDialog.info.data.opponent} />
</div>
</div>
</div>
);
}
}
})()}
</div>
);
}
};
import DialogMessagesLayout from '../layouts/DialogMessagesLayout';
import React from 'react';
import {connect} from 'react-redux';
import {fetchMessages, retrySendMessage} from '../actions/DialogsActions';
import {bindActionCreators} from 'redux';
import {Loader} from 'react-loaders';
import Immutable from 'immutable';
@connect((state, ownProps) => ({
user: state.get('user').toJS(),
failedMessages: state.getIn(['dialogs', 'failedMessages', ownProps.opponent.id], Immutable.List()).toJS()
}), (dispatch, ownProps) => ({
fetchMessages: bindActionCreators((limit, offset) => fetchMessages(ownProps.opponent.id, limit, offset), dispatch),
retrySendMessage: bindActionCreators((key, text) => retrySendMessage(ownProps.opponent, key, text), dispatch)
}))
export default class DialogMessagesLayoutContainer extends React.Component {
static propTypes = {
fetchMessages: React.PropTypes.func.isRequired,
data: React.PropTypes.array.isRequired,
user: React.PropTypes.object.isRequired
};
componentDidMount() {
console.log('messages mounted');
this.props.fetchMessages(10, this.props.data.length);
}
componentWillUnmount() {
console.log('unmount');
}
render() {
if (this.props.user.loading) {
return <Loader className="colored text-center" type="line-scale" />;
} else {
return <DialogMessagesLayout {...this.props} />
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment