Merge pull request #410 from jorilallo/remove-set-state
Removes all uses of setState
This commit is contained in:
@ -1,5 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { observable } from 'mobx';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import Popover from 'components/Popover';
|
import Popover from 'components/Popover';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@ -27,13 +28,10 @@ type Props = {
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
class DocumentViews extends Component {
|
class DocumentViews extends Component {
|
||||||
|
@observable opened: boolean = false;
|
||||||
anchor: HTMLElement;
|
anchor: HTMLElement;
|
||||||
store: DocumentViewersStore;
|
store: DocumentViewersStore;
|
||||||
props: Props;
|
props: Props;
|
||||||
state: {
|
|
||||||
opened: boolean,
|
|
||||||
};
|
|
||||||
state = {};
|
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -41,11 +39,11 @@ class DocumentViews extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openPopover = () => {
|
openPopover = () => {
|
||||||
this.setState({ opened: true });
|
this.opened = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
closePopover = () => {
|
closePopover = () => {
|
||||||
this.setState({ opened: false });
|
this.opened = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
setRef = (ref: HTMLElement) => {
|
setRef = (ref: HTMLElement) => {
|
||||||
@ -58,7 +56,7 @@ class DocumentViews extends Component {
|
|||||||
<a ref={this.setRef} onClick={this.openPopover}>
|
<a ref={this.setRef} onClick={this.openPopover}>
|
||||||
Viewed {this.props.count} {this.props.count === 1 ? 'time' : 'times'}
|
Viewed {this.props.count} {this.props.count === 1 ? 'time' : 'times'}
|
||||||
</a>
|
</a>
|
||||||
{this.state.opened && (
|
{this.opened && (
|
||||||
<Popover anchor={this.anchor} onClose={this.closePopover}>
|
<Popover anchor={this.anchor} onClose={this.closePopover}>
|
||||||
<DocumentViewers
|
<DocumentViewers
|
||||||
onMount={this.store.fetchViewers}
|
onMount={this.store.fetchViewers}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { inject } from 'mobx-react';
|
import { observable } from 'mobx';
|
||||||
|
import { observer, inject } from 'mobx-react';
|
||||||
import { injectGlobal } from 'styled-components';
|
import { injectGlobal } from 'styled-components';
|
||||||
import { color } from 'shared/styles/constants';
|
import { color } from 'shared/styles/constants';
|
||||||
import invariant from 'invariant';
|
import invariant from 'invariant';
|
||||||
@ -22,6 +23,7 @@ type Props = {
|
|||||||
history: Object,
|
history: Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
injectGlobal`
|
injectGlobal`
|
||||||
.activeDropZone {
|
.activeDropZone {
|
||||||
background: ${color.slateDark};
|
background: ${color.slateDark};
|
||||||
@ -33,14 +35,10 @@ injectGlobal`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@observer
|
||||||
class DropToImport extends Component {
|
class DropToImport extends Component {
|
||||||
state: {
|
@observable isImporting: boolean = false;
|
||||||
isImporting: boolean,
|
|
||||||
};
|
|
||||||
props: Props;
|
props: Props;
|
||||||
state = {
|
|
||||||
isImporting: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
importFile = async ({ file, documentId, collectionId, redirect }) => {
|
importFile = async ({ file, documentId, collectionId, redirect }) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
@ -67,7 +65,7 @@ class DropToImport extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onDropAccepted = async (files = []) => {
|
onDropAccepted = async (files = []) => {
|
||||||
this.setState({ isImporting: true });
|
this.isImporting = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let collectionId = this.props.collectionId;
|
let collectionId = this.props.collectionId;
|
||||||
@ -86,7 +84,7 @@ class DropToImport extends Component {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
// TODO: show error alert.
|
// TODO: show error alert.
|
||||||
} finally {
|
} finally {
|
||||||
this.setState({ isImporting: false });
|
this.isImporting = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -115,7 +113,7 @@ class DropToImport extends Component {
|
|||||||
ref={this.props.dropzoneRef}
|
ref={this.props.dropzoneRef}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{this.state.isImporting && <LoadingIndicator />}
|
{this.isImporting && <LoadingIndicator />}
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</Dropzone>
|
</Dropzone>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { observable } from 'mobx';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import { Portal } from 'react-portal';
|
import { Portal } from 'react-portal';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
@ -7,28 +9,20 @@ import type { State } from '../../types';
|
|||||||
import FormattingToolbar from './components/FormattingToolbar';
|
import FormattingToolbar from './components/FormattingToolbar';
|
||||||
import LinkToolbar from './components/LinkToolbar';
|
import LinkToolbar from './components/LinkToolbar';
|
||||||
|
|
||||||
|
@observer
|
||||||
export default class Toolbar extends Component {
|
export default class Toolbar extends Component {
|
||||||
|
@observable active: boolean = false;
|
||||||
|
@observable focused: boolean = false;
|
||||||
|
@observable link: ?React$Element<any>;
|
||||||
|
@observable top: string = '';
|
||||||
|
@observable left: string = '';
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
state: State,
|
state: State,
|
||||||
onChange: Function,
|
onChange: (state: State) => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
menu: HTMLElement;
|
menu: HTMLElement;
|
||||||
state: {
|
|
||||||
active: boolean,
|
|
||||||
focused: boolean,
|
|
||||||
link: React$Element<any>,
|
|
||||||
top: string,
|
|
||||||
left: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
active: false,
|
|
||||||
focused: false,
|
|
||||||
link: null,
|
|
||||||
top: '',
|
|
||||||
left: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount = () => {
|
componentDidMount = () => {
|
||||||
this.update();
|
this.update();
|
||||||
@ -39,11 +33,11 @@ export default class Toolbar extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleFocus = () => {
|
handleFocus = () => {
|
||||||
this.setState({ focused: true });
|
this.focused = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
handleBlur = () => {
|
handleBlur = () => {
|
||||||
this.setState({ focused: false });
|
this.focused = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
get linkInSelection(): any {
|
get linkInSelection(): any {
|
||||||
@ -66,8 +60,11 @@ export default class Toolbar extends Component {
|
|||||||
const link = this.linkInSelection;
|
const link = this.linkInSelection;
|
||||||
|
|
||||||
if (state.isBlurred || (state.isCollapsed && !link)) {
|
if (state.isBlurred || (state.isCollapsed && !link)) {
|
||||||
if (this.state.active && !this.state.focused) {
|
if (this.active && !this.focused) {
|
||||||
this.setState({ active: false, link: null, top: '', left: '' });
|
this.active = false;
|
||||||
|
this.link = undefined;
|
||||||
|
this.top = '';
|
||||||
|
this.left = '';
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -79,32 +76,25 @@ export default class Toolbar extends Component {
|
|||||||
// don't display toolbar for code blocks
|
// don't display toolbar for code blocks
|
||||||
if (state.startBlock.type === 'code') return;
|
if (state.startBlock.type === 'code') return;
|
||||||
|
|
||||||
const data = {
|
this.active = true;
|
||||||
...this.state,
|
this.focused = !!link;
|
||||||
active: true,
|
this.link = link;
|
||||||
link,
|
|
||||||
focused: !!link,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!_.isEqual(data, this.state)) {
|
const padding = 16;
|
||||||
const padding = 16;
|
const selection = window.getSelection();
|
||||||
const selection = window.getSelection();
|
const range = selection.getRangeAt(0);
|
||||||
const range = selection.getRangeAt(0);
|
const rect = range.getBoundingClientRect();
|
||||||
const rect = range.getBoundingClientRect();
|
|
||||||
|
|
||||||
if (rect.top === 0 && rect.left === 0) {
|
if (rect.top === 0 && rect.left === 0) {
|
||||||
this.setState(data);
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const left =
|
|
||||||
rect.left + window.scrollX - this.menu.offsetWidth / 2 + rect.width / 2;
|
|
||||||
data.top = `${Math.round(
|
|
||||||
rect.top + window.scrollY - this.menu.offsetHeight
|
|
||||||
)}px`;
|
|
||||||
data.left = `${Math.round(Math.max(padding, left))}px`;
|
|
||||||
this.setState(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const left =
|
||||||
|
rect.left + window.scrollX - this.menu.offsetWidth / 2 + rect.width / 2;
|
||||||
|
this.top = `${Math.round(
|
||||||
|
rect.top + window.scrollY - this.menu.offsetHeight
|
||||||
|
)}px`;
|
||||||
|
this.left = `${Math.round(Math.max(padding, left))}px`;
|
||||||
};
|
};
|
||||||
|
|
||||||
setRef = (ref: HTMLElement) => {
|
setRef = (ref: HTMLElement) => {
|
||||||
@ -112,20 +102,21 @@ export default class Toolbar extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const link = this.state.link;
|
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
top: this.state.top,
|
top: this.top,
|
||||||
left: this.state.left,
|
left: this.left,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<Menu active={this.state.active} innerRef={this.setRef} style={style}>
|
<Menu active={this.active} innerRef={this.setRef} style={style}>
|
||||||
{link && (
|
{this.link ? (
|
||||||
<LinkToolbar {...this.props} link={link} onBlur={this.handleBlur} />
|
<LinkToolbar
|
||||||
)}
|
{...this.props}
|
||||||
{!link && (
|
link={this.link}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<FormattingToolbar
|
<FormattingToolbar
|
||||||
onCreateLink={this.handleFocus}
|
onCreateLink={this.handleFocus}
|
||||||
{...this.props}
|
{...this.props}
|
||||||
|
@ -15,6 +15,7 @@ type Props = {
|
|||||||
onRequestClose: () => void,
|
onRequestClose: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
injectGlobal`
|
injectGlobal`
|
||||||
.ReactModal__Overlay {
|
.ReactModal__Overlay {
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import { observable } from 'mobx';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@ -15,7 +16,7 @@ import { Label } from 'components/Labeled';
|
|||||||
import SlackAuthLink from 'components/SlackAuthLink';
|
import SlackAuthLink from 'components/SlackAuthLink';
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class Settings extends React.Component {
|
class Settings extends Component {
|
||||||
store: SettingsStore;
|
store: SettingsStore;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -89,7 +90,8 @@ class Settings extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InlineForm extends React.Component {
|
@observer
|
||||||
|
class InlineForm extends Component {
|
||||||
props: {
|
props: {
|
||||||
placeholder: string,
|
placeholder: string,
|
||||||
buttonLabel: string,
|
buttonLabel: string,
|
||||||
@ -99,11 +101,9 @@ class InlineForm extends React.Component {
|
|||||||
onSubmit: Function,
|
onSubmit: Function,
|
||||||
disabled?: ?boolean,
|
disabled?: ?boolean,
|
||||||
};
|
};
|
||||||
validationTimeout: number;
|
|
||||||
|
|
||||||
state = {
|
@observable validationError: boolean = false;
|
||||||
validationError: false,
|
validationTimeout: number;
|
||||||
};
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
clearTimeout(this.validationTimeout);
|
clearTimeout(this.validationTimeout);
|
||||||
@ -114,14 +114,9 @@ class InlineForm extends React.Component {
|
|||||||
if (this.props.value) {
|
if (this.props.value) {
|
||||||
this.props.onSubmit();
|
this.props.onSubmit();
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.validationError = true;
|
||||||
validationError: true,
|
|
||||||
});
|
|
||||||
this.validationTimeout = setTimeout(
|
this.validationTimeout = setTimeout(
|
||||||
() =>
|
() => (this.validationError = false),
|
||||||
this.setState({
|
|
||||||
validationError: false,
|
|
||||||
}),
|
|
||||||
2500
|
2500
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -129,17 +124,18 @@ class InlineForm extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { placeholder, value, onChange, buttonLabel } = this.props;
|
const { placeholder, value, onChange, buttonLabel } = this.props;
|
||||||
const { validationError } = this.state;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={this.handleSubmit}>
|
<form onSubmit={this.handleSubmit}>
|
||||||
<Flex auto>
|
<Flex auto>
|
||||||
<ApiKeyInput
|
<ApiKeyInput
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={validationError ? 'Please add a label' : placeholder}
|
placeholder={
|
||||||
|
this.validationError ? 'Please add a label' : placeholder
|
||||||
|
}
|
||||||
value={value || ''}
|
value={value || ''}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
validationError={validationError}
|
validationError={this.validationError}
|
||||||
/>
|
/>
|
||||||
<Button type="submit" value={buttonLabel} />
|
<Button type="submit" value={buttonLabel} />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
Reference in New Issue
Block a user