diff --git a/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js
index 05616e444826cbaf4a8c65dfa30d3ef0f5e578a3..80aa33a187ad819f036512e1ae7b95fe4a13a743 100644
--- a/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js
+++ b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import renderer from 'react-test-renderer';
 import AutosuggestEmoji from '../autosuggest_emoji';
 
diff --git a/app/javascript/mastodon/components/__tests__/avatar-test.js b/app/javascript/mastodon/components/__tests__/avatar-test.js
index dd3f7b7d2104f06a54c825bf4e495360f7f70b5e..d4f81e273d5b91a6b566d2ac8bcb83219388b5ad 100644
--- a/app/javascript/mastodon/components/__tests__/avatar-test.js
+++ b/app/javascript/mastodon/components/__tests__/avatar-test.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import renderer from 'react-test-renderer';
 import { fromJS } from 'immutable';
 import Avatar from '../avatar';
diff --git a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js
index 44addea832f7231df7a1648fbff484cbfcf2623d..a5b418c6c986189b48d991852a67f31e4ef94638 100644
--- a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js
+++ b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import renderer from 'react-test-renderer';
 import { fromJS } from 'immutable';
 import AvatarOverlay from '../avatar_overlay';
diff --git a/app/javascript/mastodon/components/__tests__/button-test.js b/app/javascript/mastodon/components/__tests__/button-test.js
index f5a649f70e32ccfc1a2f9b32b0bf605e92ce9d7a..82d307e84302ccde4ef0d0cde32eb6a7a25d5bb8 100644
--- a/app/javascript/mastodon/components/__tests__/button-test.js
+++ b/app/javascript/mastodon/components/__tests__/button-test.js
@@ -1,5 +1,4 @@
 import { render, fireEvent, screen } from '@testing-library/react';
-import React from 'react';
 import renderer from 'react-test-renderer';
 import Button from '../button';
 
diff --git a/app/javascript/mastodon/components/__tests__/display_name-test.js b/app/javascript/mastodon/components/__tests__/display_name-test.js
index 0d040c4cd8cc04661fd4ebcb02b04d1c746647b9..2506cc78002508a910e4288d6f0fd9d1a7847aad 100644
--- a/app/javascript/mastodon/components/__tests__/display_name-test.js
+++ b/app/javascript/mastodon/components/__tests__/display_name-test.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import renderer from 'react-test-renderer';
 import { fromJS }  from 'immutable';
 import DisplayName from '../display_name';
diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.js
index af9f119c82c173e352fab211c77ab2951d76b3ef..98709742c48915576e412f0ebd35b0073b5b307a 100644
--- a/app/javascript/mastodon/components/account.js
+++ b/app/javascript/mastodon/components/account.js
@@ -1,4 +1,3 @@
-import React, { Fragment } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from './avatar';
@@ -72,10 +71,10 @@ class Account extends ImmutablePureComponent {
 
     if (hidden) {
       return (
-        <Fragment>
+        <>
           {account.get('display_name')}
           {account.get('username')}
-        </Fragment>
+        </>
       );
     }
 
@@ -103,10 +102,10 @@ class Account extends ImmutablePureComponent {
           hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username')  })} onClick={this.handleMuteNotifications} />;
         }
         buttons = (
-          <Fragment>
+          <>
             <IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />
             {hidingNotificationsButton}
-          </Fragment>
+          </>
         );
       } else if (defaultAction === 'mute') {
         buttons = <IconButton icon='volume-off' title={intl.formatMessage(messages.mute, { name: account.get('username') })} onClick={this.handleMute} />;
diff --git a/app/javascript/mastodon/components/admin/Counter.js b/app/javascript/mastodon/components/admin/Counter.js
index 5a5b2b869e078e099e81b7c6dc4ecec8df08bcbf..9eaafbca889a3a9375a558482d2ade86e3d0dece 100644
--- a/app/javascript/mastodon/components/admin/Counter.js
+++ b/app/javascript/mastodon/components/admin/Counter.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import api from 'mastodon/api';
 import { FormattedNumber } from 'react-intl';
@@ -24,7 +24,7 @@ const percIncrease = (a, b) => {
   return percent;
 };
 
-export default class Counter extends React.PureComponent {
+export default class Counter extends PureComponent {
 
   static propTypes = {
     measure: PropTypes.string.isRequired,
@@ -62,25 +62,25 @@ export default class Counter extends React.PureComponent {
 
     if (loading) {
       content = (
-        <React.Fragment>
+        <>
           <span className='sparkline__value__total'><Skeleton width={43} /></span>
           <span className='sparkline__value__change'><Skeleton width={43} /></span>
-        </React.Fragment>
+        </>
       );
     } else {
       const measure = data[0];
       const percentChange = measure.previous_total && percIncrease(measure.previous_total * 1, measure.total * 1);
 
       content = (
-        <React.Fragment>
+        <>
           <span className='sparkline__value__total'>{measure.human_value || <FormattedNumber value={measure.total} />}</span>
           {measure.previous_total && (<span className={classNames('sparkline__value__change', { positive: percentChange > 0, negative: percentChange < 0 })}>{percentChange > 0 && '+'}<FormattedNumber value={percentChange} style='percent' /></span>)}
-        </React.Fragment>
+        </>
       );
     }
 
     const inner = (
-      <React.Fragment>
+      <>
         <div className='sparkline__value'>
           {content}
         </div>
@@ -96,7 +96,7 @@ export default class Counter extends React.PureComponent {
             </Sparklines>
           )}
         </div>
-      </React.Fragment>
+      </>
     );
 
     if (href) {
diff --git a/app/javascript/mastodon/components/admin/Dimension.js b/app/javascript/mastodon/components/admin/Dimension.js
index 977c8208df18c4307791e0fb8a74d0bd273a26c5..090cea9fcb88c9d46ebbab721ac2424ea172feb2 100644
--- a/app/javascript/mastodon/components/admin/Dimension.js
+++ b/app/javascript/mastodon/components/admin/Dimension.js
@@ -1,11 +1,11 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import api from 'mastodon/api';
 import { FormattedNumber } from 'react-intl';
 import { roundTo10 } from 'mastodon/utils/numbers';
 import Skeleton from 'mastodon/components/skeleton';
 
-export default class Dimension extends React.PureComponent {
+export default class Dimension extends PureComponent {
 
   static propTypes = {
     dimension: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/components/admin/ReportReasonSelector.js b/app/javascript/mastodon/components/admin/ReportReasonSelector.js
index 1f91d25175a02bf78039848f6db206bc19f42a20..3cdff8997fdbfdbbfa6919c2c31fdfc946a4faab 100644
--- a/app/javascript/mastodon/components/admin/ReportReasonSelector.js
+++ b/app/javascript/mastodon/components/admin/ReportReasonSelector.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import api from 'mastodon/api';
 import { injectIntl, defineMessages } from 'react-intl';
@@ -10,7 +10,7 @@ const messages = defineMessages({
   violation: { id: 'report.categories.violation', defaultMessage: 'Content violates one or more server rules' },
 });
 
-class Category extends React.PureComponent {
+class Category extends PureComponent {
 
   static propTypes = {
     id: PropTypes.string.isRequired,
@@ -52,7 +52,7 @@ class Category extends React.PureComponent {
 
 }
 
-class Rule extends React.PureComponent {
+class Rule extends PureComponent {
 
   static propTypes = {
     id: PropTypes.string.isRequired,
@@ -85,7 +85,7 @@ class Rule extends React.PureComponent {
 }
 
 export default @injectIntl
-class ReportReasonSelector extends React.PureComponent {
+class ReportReasonSelector extends PureComponent {
 
   static propTypes = {
     id: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/components/admin/Retention.js b/app/javascript/mastodon/components/admin/Retention.js
index 47c9e715136ced69dce195dec5e0c2b2795586a3..c724bd5ce1ca1c896669a2c68285676e77439aa3 100644
--- a/app/javascript/mastodon/components/admin/Retention.js
+++ b/app/javascript/mastodon/components/admin/Retention.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import api from 'mastodon/api';
 import { FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
@@ -14,7 +14,7 @@ const dateForCohort = cohort => {
   }
 };
 
-export default class Retention extends React.PureComponent {
+export default class Retention extends PureComponent {
 
   static propTypes = {
     start_at: PropTypes.string,
diff --git a/app/javascript/mastodon/components/admin/Trends.js b/app/javascript/mastodon/components/admin/Trends.js
index 635bdf37d56d1c0f161b09ac4c3ff728d0092b72..bb7777a542b04a4c1245ad622b045ce4f16ee154 100644
--- a/app/javascript/mastodon/components/admin/Trends.js
+++ b/app/javascript/mastodon/components/admin/Trends.js
@@ -1,11 +1,11 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import api from 'mastodon/api';
 import { FormattedMessage } from 'react-intl';
 import classNames from 'classnames';
 import Hashtag from 'mastodon/components/hashtag';
 
-export default class Trends extends React.PureComponent {
+export default class Trends extends PureComponent {
 
   static propTypes = {
     limit: PropTypes.number.isRequired,
diff --git a/app/javascript/mastodon/components/animated_number.js b/app/javascript/mastodon/components/animated_number.js
index fbe948c5b02371dc824951e78e13478b2a601f4e..46bb82dafcca2718a8a1849bfdf28b6be9cb73c7 100644
--- a/app/javascript/mastodon/components/animated_number.js
+++ b/app/javascript/mastodon/components/animated_number.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedNumber } from 'react-intl';
 import TransitionMotion from 'react-motion/lib/TransitionMotion';
@@ -15,7 +15,7 @@ const obfuscatedCount = count => {
   }
 };
 
-export default class AnimatedNumber extends React.PureComponent {
+export default class AnimatedNumber extends PureComponent {
 
   static propTypes = {
     value: PropTypes.number.isRequired,
@@ -26,7 +26,7 @@ export default class AnimatedNumber extends React.PureComponent {
     direction: 1,
   };
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (nextProps.value > this.props.value) {
       this.setState({ direction: 1 });
     } else if (nextProps.value < this.props.value) {
diff --git a/app/javascript/mastodon/components/attachment_list.js b/app/javascript/mastodon/components/attachment_list.js
index 0e23889dedc75ef60ccaf047c30bfea24203e69b..e65c7956b58d383a9afd9789d1e877b3912130bc 100644
--- a/app/javascript/mastodon/components/attachment_list.js
+++ b/app/javascript/mastodon/components/attachment_list.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/components/autosuggest_emoji.js b/app/javascript/mastodon/components/autosuggest_emoji.js
index 4937e4d9845f4d46221fe002f2936d91460efe62..8afbdd9cb5c658d2ad8d876ac9cb53a6e5bf28df 100644
--- a/app/javascript/mastodon/components/autosuggest_emoji.js
+++ b/app/javascript/mastodon/components/autosuggest_emoji.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import unicodeMapping from '../features/emoji/emoji_unicode_mapping_light';
 import { assetHost } from 'mastodon/utils/config';
 
-export default class AutosuggestEmoji extends React.PureComponent {
+export default class AutosuggestEmoji extends PureComponent {
 
   static propTypes = {
     emoji: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/components/autosuggest_hashtag.js b/app/javascript/mastodon/components/autosuggest_hashtag.js
index 9e9d888f83b48eb4b6ebe68daa0bfdccbc9289c0..4fd23ec9a8be3e534f7ca5ef8120acfe5a5aaff1 100644
--- a/app/javascript/mastodon/components/autosuggest_hashtag.js
+++ b/app/javascript/mastodon/components/autosuggest_hashtag.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ShortNumber from 'mastodon/components/short_number';
 import { FormattedMessage } from 'react-intl';
 
-export default class AutosuggestHashtag extends React.PureComponent {
+export default class AutosuggestHashtag extends PureComponent {
 
   static propTypes = {
     tag: PropTypes.shape({
diff --git a/app/javascript/mastodon/components/autosuggest_input.js b/app/javascript/mastodon/components/autosuggest_input.js
index 12d44b5d0b27cbc4b57a00a3fb5a20a73e1c80d8..5c6ac019c60bcf6ab8cf9307a797cc66338cd3d3 100644
--- a/app/javascript/mastodon/components/autosuggest_input.js
+++ b/app/javascript/mastodon/components/autosuggest_input.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
 import AutosuggestEmoji from './autosuggest_emoji';
 import AutosuggestHashtag from './autosuggest_hashtag';
@@ -152,7 +151,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     this.input.focus();
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
       this.setState({ suggestionsHidden: false });
     }
diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js
index 08b9cd80bbf7aaaec010887e64e638195b9afc59..7d17107be002b0c9fe7e10ad880c4c2cf7cf4254 100644
--- a/app/javascript/mastodon/components/autosuggest_textarea.js
+++ b/app/javascript/mastodon/components/autosuggest_textarea.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
 import AutosuggestEmoji from './autosuggest_emoji';
 import AutosuggestHashtag from './autosuggest_hashtag';
@@ -152,7 +151,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     this.textarea.focus();
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
       this.setState({ suggestionsHidden: false });
     }
diff --git a/app/javascript/mastodon/components/avatar.js b/app/javascript/mastodon/components/avatar.js
index 12ab7d2dfe410d73914ec412b6b684eeebf75ffb..3394a8e265581b73cbe01b7d097ec56daa5a290c 100644
--- a/app/javascript/mastodon/components/avatar.js
+++ b/app/javascript/mastodon/components/avatar.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { autoPlayGif } from '../initial_state';
 import classNames from 'classnames';
 
-export default class Avatar extends React.PureComponent {
+export default class Avatar extends PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map,
diff --git a/app/javascript/mastodon/components/avatar_composite.js b/app/javascript/mastodon/components/avatar_composite.js
index 5d5b897492fec082ba26453f98153b293a096b12..2a49bf4264b53e13138b8f7079bdb3ab64451374 100644
--- a/app/javascript/mastodon/components/avatar_composite.js
+++ b/app/javascript/mastodon/components/avatar_composite.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { autoPlayGif } from '../initial_state';
 
-export default class AvatarComposite extends React.PureComponent {
+export default class AvatarComposite extends PureComponent {
 
   static propTypes = {
     accounts: ImmutablePropTypes.list.isRequired,
diff --git a/app/javascript/mastodon/components/avatar_overlay.js b/app/javascript/mastodon/components/avatar_overlay.js
index 3ec1d77304f585365ae3cc9827ee6f2ddf5d7592..0de08ef03613bacc41ae44de0e19842d45c4d073 100644
--- a/app/javascript/mastodon/components/avatar_overlay.js
+++ b/app/javascript/mastodon/components/avatar_overlay.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { autoPlayGif } from '../initial_state';
 
-export default class AvatarOverlay extends React.PureComponent {
+export default class AvatarOverlay extends PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/components/blurhash.js b/app/javascript/mastodon/components/blurhash.js
index 2af5cfc5685f8127d8331f5328cb6ff49bafb64c..21fbebc70ab84f4d66186eb1b5a79d2024b8c81a 100644
--- a/app/javascript/mastodon/components/blurhash.js
+++ b/app/javascript/mastodon/components/blurhash.js
@@ -1,7 +1,7 @@
 // @ts-check
 
 import { decode } from 'blurhash';
-import React, { useRef, useEffect } from 'react';
+import { memo, useRef, useEffect } from 'react';
 import PropTypes from 'prop-types';
 
 /**
@@ -62,4 +62,4 @@ Blurhash.propTypes = {
   dummy: PropTypes.bool,
 };
 
-export default React.memo(Blurhash);
+export default memo(Blurhash);
diff --git a/app/javascript/mastodon/components/button.js b/app/javascript/mastodon/components/button.js
index 85b2d78ca9ee69702320ef7ad58063c915cfae2b..f153d8c9ccd6740303040cd3d345c1e911576098 100644
--- a/app/javascript/mastodon/components/button.js
+++ b/app/javascript/mastodon/components/button.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 
-export default class Button extends React.PureComponent {
+export default class Button extends PureComponent {
 
   static propTypes = {
     text: PropTypes.node,
diff --git a/app/javascript/mastodon/components/check.js b/app/javascript/mastodon/components/check.js
index ee2ef1595a2d034b9372f4801e2a200005c5441c..d818480b7ba75b62f8de4b724c4297a6e755bc50 100644
--- a/app/javascript/mastodon/components/check.js
+++ b/app/javascript/mastodon/components/check.js
@@ -1,5 +1,3 @@
-import React from 'react';
-
 const Check = () => (
   <svg width='14' height='11' viewBox='0 0 14 11'>
     <path d='M11.264 0L5.26 6.004 2.103 2.847 0 4.95l5.26 5.26 8.108-8.107L11.264 0' fill='currentColor' fillRule='evenodd' />
diff --git a/app/javascript/mastodon/components/column.js b/app/javascript/mastodon/components/column.js
index 239824a4fec593385804cc05602376c242bde1ff..94ffa998e38d308943c626580a3397640566fe0a 100644
--- a/app/javascript/mastodon/components/column.js
+++ b/app/javascript/mastodon/components/column.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { supportsPassiveEvents } from 'detect-passive-events';
 import { scrollTop } from '../scroll';
 
-export default class Column extends React.PureComponent {
+export default class Column extends PureComponent {
 
   static propTypes = {
     children: PropTypes.node,
diff --git a/app/javascript/mastodon/components/column_back_button.js b/app/javascript/mastodon/components/column_back_button.js
index d97622705e588b8951cbf0197c714ae0a37fe07e..754afb1bdf92be8b8bee00b1e18a1cbc24a633a6 100644
--- a/app/javascript/mastodon/components/column_back_button.js
+++ b/app/javascript/mastodon/components/column_back_button.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
 import Icon from 'mastodon/components/icon';
 import { createPortal } from 'react-dom';
 
-export default class ColumnBackButton extends React.PureComponent {
+export default class ColumnBackButton extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/components/column_back_button_slim.js b/app/javascript/mastodon/components/column_back_button_slim.js
index cc8bfb1515ba53b11bc4811d98663db737fede77..64476351786246b33c94f0af1261aed09df9e7f2 100644
--- a/app/javascript/mastodon/components/column_back_button_slim.js
+++ b/app/javascript/mastodon/components/column_back_button_slim.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { FormattedMessage } from 'react-intl';
 import ColumnBackButton from './column_back_button';
 import Icon from 'mastodon/components/icon';
diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js
index cbbc490a83b9d7cedaffc477420590b9fd2ba0db..51d806e4ee12502b394ce1d67c45b703b54f88b6 100644
--- a/app/javascript/mastodon/components/column_header.js
+++ b/app/javascript/mastodon/components/column_header.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { createPortal } from 'react-dom';
 import classNames from 'classnames';
@@ -13,7 +13,7 @@ const messages = defineMessages({
 });
 
 export default @injectIntl
-class ColumnHeader extends React.PureComponent {
+class ColumnHeader extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/components/common_counter.js b/app/javascript/mastodon/components/common_counter.js
index dd9b62de9b1b3dac7ff1118b0c4f7e6579e131b0..5d169c9332e94a1712c1bb13a5b5153f73d89555 100644
--- a/app/javascript/mastodon/components/common_counter.js
+++ b/app/javascript/mastodon/components/common_counter.js
@@ -1,5 +1,4 @@
 // @ts-check
-import React from 'react';
 import { FormattedMessage } from 'react-intl';
 
 /**
diff --git a/app/javascript/mastodon/components/display_name.js b/app/javascript/mastodon/components/display_name.js
index 7ccfbd0cd5f1c7fda9fafcd71f35ec669e641c55..df6136e94831918f998873c0719eb55307c35b9a 100644
--- a/app/javascript/mastodon/components/display_name.js
+++ b/app/javascript/mastodon/components/display_name.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { autoPlayGif } from 'mastodon/initial_state';
 
-export default class DisplayName extends React.PureComponent {
+export default class DisplayName extends PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/components/domain.js b/app/javascript/mastodon/components/domain.js
index 697065d874d1f6f58c2c1a6c6a6ed9a144ac608c..935d260f4869eb5b3981a7d521ec77b6287ab48c 100644
--- a/app/javascript/mastodon/components/domain.js
+++ b/app/javascript/mastodon/components/domain.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import IconButton from './icon_button';
 import { defineMessages, injectIntl } from 'react-intl';
diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js
index 4b4ad83555ff08579ca718b1e01051365ce97a50..f29d4eb539df3af9ab13d53b7d9c90eb59f6ad50 100644
--- a/app/javascript/mastodon/components/dropdown_menu.js
+++ b/app/javascript/mastodon/components/dropdown_menu.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { Children, PureComponent, cloneElement } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import IconButton from './icon_button';
@@ -12,7 +12,7 @@ import { CircularProgress } from 'mastodon/components/loading_indicator';
 const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
 let id = 0;
 
-class DropdownMenu extends React.PureComponent {
+class DropdownMenu extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
@@ -178,7 +178,7 @@ class DropdownMenu extends React.PureComponent {
 
 }
 
-export default class Dropdown extends React.PureComponent {
+export default class Dropdown extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
@@ -313,7 +313,7 @@ export default class Dropdown extends React.PureComponent {
 
     const open = this.state.id === openDropdownId;
 
-    const button = children ? React.cloneElement(React.Children.only(children), {
+    const button = children ? cloneElement(Children.only(children), {
       ref: this.setTargetRef,
       onClick: this.handleClick,
       onMouseDown: this.handleMouseDown,
@@ -335,7 +335,7 @@ export default class Dropdown extends React.PureComponent {
     );
 
     return (
-      <React.Fragment>
+      <>
         {button}
 
         <Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
@@ -350,7 +350,7 @@ export default class Dropdown extends React.PureComponent {
             onItemClick={this.handleItemClick}
           />
         </Overlay>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/components/edited_timestamp/index.js b/app/javascript/mastodon/components/edited_timestamp/index.js
index bebf938865bc9837211dd17560ef44a7a2651490..679ec4e4cd5c175c304dbaa329517bde2a4f128a 100644
--- a/app/javascript/mastodon/components/edited_timestamp/index.js
+++ b/app/javascript/mastodon/components/edited_timestamp/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage, injectIntl } from 'react-intl';
 import Icon from 'mastodon/components/icon';
@@ -18,7 +18,7 @@ const mapDispatchToProps = (dispatch, { statusId }) => ({
 
 export default @connect(null, mapDispatchToProps)
 @injectIntl
-class EditedTimestamp extends React.PureComponent {
+class EditedTimestamp extends PureComponent {
 
   static propTypes = {
     statusId: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/components/error_boundary.js b/app/javascript/mastodon/components/error_boundary.js
index ca4a2cfe14bed1d06b704db422bce6783929c444..7ca9facd2aa62b9120dc9e41ff4c5dd511224209 100644
--- a/app/javascript/mastodon/components/error_boundary.js
+++ b/app/javascript/mastodon/components/error_boundary.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
 import { version, source_url } from 'mastodon/initial_state';
 import StackTrace from 'stacktrace-js';
 
-export default class ErrorBoundary extends React.PureComponent {
+export default class ErrorBoundary extends PureComponent {
 
   static propTypes = {
     children: PropTypes.node,
diff --git a/app/javascript/mastodon/components/gifv.js b/app/javascript/mastodon/components/gifv.js
index b775e52005b417f9f1b3f09edd94a77be2c3878b..848411a11953037d024901e7508cfbd927214d7d 100644
--- a/app/javascript/mastodon/components/gifv.js
+++ b/app/javascript/mastodon/components/gifv.js
@@ -1,7 +1,7 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 
-export default class GIFV extends React.PureComponent {
+export default class GIFV extends PureComponent {
 
   static propTypes = {
     src: PropTypes.string.isRequired,
@@ -19,7 +19,7 @@ export default class GIFV extends React.PureComponent {
     this.setState({ loading: false });
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (nextProps.src !== this.props.src) {
       this.setState({ loading: true });
     }
diff --git a/app/javascript/mastodon/components/hashtag.js b/app/javascript/mastodon/components/hashtag.js
index 7f442d189ca561d5ac75831433f290428e5dad45..5fee2bfc8778b850745ddbd518fc70518506f5b6 100644
--- a/app/javascript/mastodon/components/hashtag.js
+++ b/app/javascript/mastodon/components/hashtag.js
@@ -1,5 +1,5 @@
 // @ts-check
-import React from 'react';
+import { Component } from 'react';
 import { Sparklines, SparklinesCurve } from 'react-sparklines';
 import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
@@ -9,7 +9,7 @@ import ShortNumber from 'mastodon/components/short_number';
 import Skeleton from 'mastodon/components/skeleton';
 import classNames from 'classnames';
 
-class SilentErrorBoundary extends React.Component {
+class SilentErrorBoundary extends Component {
 
   static propTypes = {
     children: PropTypes.node,
@@ -68,7 +68,7 @@ const Hashtag = ({ name, href, to, people, uses, history, className }) => (
   <div className={classNames('trends__item', className)}>
     <div className='trends__item__name'>
       <Permalink href={href} to={to}>
-        {name ? <React.Fragment>#<span>{name}</span></React.Fragment> : <Skeleton width={50} />}
+        {name ? <>#<span>{name}</span></> : <Skeleton width={50} />}
       </Permalink>
 
       {typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />}
diff --git a/app/javascript/mastodon/components/icon.js b/app/javascript/mastodon/components/icon.js
index d8a17722fed2a7bbcaf4060d8587f71ebdd29524..11178dd127551cf274a4c66d56f4b27c2606973d 100644
--- a/app/javascript/mastodon/components/icon.js
+++ b/app/javascript/mastodon/components/icon.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 
-export default class Icon extends React.PureComponent {
+export default class Icon extends PureComponent {
 
   static propTypes = {
     id: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js
index 81743a1dbe1ad7df6e87135944cb9e13eb14048b..1c1c3244b0645ff377f9c48cca1f8b6fd4bd692e 100644
--- a/app/javascript/mastodon/components/icon_button.js
+++ b/app/javascript/mastodon/components/icon_button.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import Icon from 'mastodon/components/icon';
 import AnimatedNumber from 'mastodon/components/animated_number';
 
-export default class IconButton extends React.PureComponent {
+export default class IconButton extends PureComponent {
 
   static propTypes = {
     className: PropTypes.string,
@@ -44,7 +44,7 @@ export default class IconButton extends React.PureComponent {
     deactivate: false,
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (!nextProps.animate) return;
 
     if (this.props.active && !nextProps.active) {
@@ -126,9 +126,9 @@ export default class IconButton extends React.PureComponent {
     }
 
     let contents = (
-      <React.Fragment>
+      <>
         <Icon id={icon} fixedWidth aria-hidden='true' /> {typeof counter !== 'undefined' && <span className='icon-button__counter'><AnimatedNumber value={counter} obfuscate={obfuscateCount} /></span>}
-      </React.Fragment>
+      </>
     );
 
     if (href) {
diff --git a/app/javascript/mastodon/components/icon_with_badge.js b/app/javascript/mastodon/components/icon_with_badge.js
index 4214eccfde9405c38ac9a6fb1ae65214f7b2866d..812ebfe5732aec6a024a185e31156359fa729df5 100644
--- a/app/javascript/mastodon/components/icon_with_badge.js
+++ b/app/javascript/mastodon/components/icon_with_badge.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import Icon from 'mastodon/components/icon';
 
diff --git a/app/javascript/mastodon/components/inline_account.js b/app/javascript/mastodon/components/inline_account.js
index a1b4955904b0011ed75e9138e343d28115978984..c5c30fb98940c0401380dd471abb4f08e183eacc 100644
--- a/app/javascript/mastodon/components/inline_account.js
+++ b/app/javascript/mastodon/components/inline_account.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
 import { makeGetAccount } from 'mastodon/selectors';
@@ -15,7 +15,7 @@ const makeMapStateToProps = () => {
 };
 
 export default @connect(makeMapStateToProps)
-class InlineAccount extends React.PureComponent {
+class InlineAccount extends PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/components/intersection_observer_article.js b/app/javascript/mastodon/components/intersection_observer_article.js
index 26f85fa4004f7c031b3c9e39280dfc3c5587495e..88a12b2425f324b63487e947f78c4aa7f12a5e56 100644
--- a/app/javascript/mastodon/components/intersection_observer_article.js
+++ b/app/javascript/mastodon/components/intersection_observer_article.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { cloneElement, Component } from 'react';
 import PropTypes from 'prop-types';
 import scheduleIdleTask from '../features/ui/util/schedule_idle_task';
 import getRectFromEntry from '../features/ui/util/get_rect_from_entry';
@@ -6,7 +6,7 @@ import getRectFromEntry from '../features/ui/util/get_rect_from_entry';
 // Diff these props in the "unrendered" state
 const updateOnPropsForUnrendered = ['id', 'index', 'listLength', 'cachedHeight'];
 
-export default class IntersectionObserverArticle extends React.Component {
+export default class IntersectionObserverArticle extends Component {
 
   static propTypes = {
     intersectionObserverWrapper: PropTypes.object.isRequired,
@@ -115,14 +115,14 @@ export default class IntersectionObserverArticle extends React.Component {
           data-id={id}
           tabIndex='0'
         >
-          {children && React.cloneElement(children, { hidden: true })}
+          {children && cloneElement(children, { hidden: true })}
         </article>
       );
     }
 
     return (
       <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex='0'>
-        {children && React.cloneElement(children, { hidden: false })}
+        {children && cloneElement(children, { hidden: false })}
       </article>
     );
   }
diff --git a/app/javascript/mastodon/components/load_gap.js b/app/javascript/mastodon/components/load_gap.js
index a44d55d09354131b37d557310fb03dbb8f300027..ca2077a239a7b620e1586756a201ea009996941e 100644
--- a/app/javascript/mastodon/components/load_gap.js
+++ b/app/javascript/mastodon/components/load_gap.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { injectIntl, defineMessages } from 'react-intl';
 import Icon from 'mastodon/components/icon';
@@ -8,7 +8,7 @@ const messages = defineMessages({
 });
 
 export default @injectIntl
-class LoadGap extends React.PureComponent {
+class LoadGap extends PureComponent {
 
   static propTypes = {
     disabled: PropTypes.bool,
diff --git a/app/javascript/mastodon/components/load_more.js b/app/javascript/mastodon/components/load_more.js
index 389c3e1e115e486806725e684f156ba553f14280..f7bd70d7da4de8940d6d71f7fafa90f084d0efa9 100644
--- a/app/javascript/mastodon/components/load_more.js
+++ b/app/javascript/mastodon/components/load_more.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
 
-export default class LoadMore extends React.PureComponent {
+export default class LoadMore extends PureComponent {
 
   static propTypes = {
     onClick: PropTypes.func,
diff --git a/app/javascript/mastodon/components/load_pending.js b/app/javascript/mastodon/components/load_pending.js
index 7e27024036a69c70a25fa227bfabbac04bf38aab..96727c7f93c3378af39f923585ef9160982eb488 100644
--- a/app/javascript/mastodon/components/load_pending.js
+++ b/app/javascript/mastodon/components/load_pending.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
 
-export default class LoadPending extends React.PureComponent {
+export default class LoadPending extends PureComponent {
 
   static propTypes = {
     onClick: PropTypes.func,
diff --git a/app/javascript/mastodon/components/loading_indicator.js b/app/javascript/mastodon/components/loading_indicator.js
index 33c59d94c02f494f75dca915d2dcd70ebe5386a1..c3f7a4e9ef0b408ecb0e5e7a62206e62f3d544d7 100644
--- a/app/javascript/mastodon/components/loading_indicator.js
+++ b/app/javascript/mastodon/components/loading_indicator.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 
 export const CircularProgress = ({ size, strokeWidth }) => {
diff --git a/app/javascript/mastodon/components/logo.js b/app/javascript/mastodon/components/logo.js
index d1c7f08a91d8df0cf91c173100e2440342deab98..26df5753d8a2ef2768774a2dafa6f4c7b7fd7423 100644
--- a/app/javascript/mastodon/components/logo.js
+++ b/app/javascript/mastodon/components/logo.js
@@ -1,5 +1,3 @@
-import React from 'react';
-
 const Logo = () => (
   <svg viewBox='0 0 216.4144 232.00976' className='logo'>
     <use xlinkHref='#mastodon-svg-logo' />
diff --git a/app/javascript/mastodon/components/media_attachments.js b/app/javascript/mastodon/components/media_attachments.js
index d27720de41c1c27a2b2d132320587a5382a2e9d5..7ebe743d634c5adb99f038c897abb9ff27f903cf 100644
--- a/app/javascript/mastodon/components/media_attachments.js
+++ b/app/javascript/mastodon/components/media_attachments.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index 2e7ce2e608f92803912e7115bd7b5b4b71c9a8f4..25d34cb58dff6fe7e6011129e19e5d0cdff14323 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { is } from 'immutable';
@@ -14,7 +14,7 @@ const messages = defineMessages({
   toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: '{number, plural, one {Hide image} other {Hide images}}' },
 });
 
-class Item extends React.PureComponent {
+class Item extends PureComponent {
 
   static propTypes = {
     attachment: ImmutablePropTypes.map.isRequired,
@@ -221,7 +221,7 @@ class Item extends React.PureComponent {
 }
 
 export default @injectIntl
-class MediaGallery extends React.PureComponent {
+class MediaGallery extends PureComponent {
 
   static propTypes = {
     sensitive: PropTypes.bool,
@@ -255,7 +255,7 @@ class MediaGallery extends React.PureComponent {
     window.removeEventListener('resize', this.handleResize);
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
       this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' });
     } else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
diff --git a/app/javascript/mastodon/components/missing_indicator.js b/app/javascript/mastodon/components/missing_indicator.js
index 7b0101bab852aa875b136c0fd38f5ca23eac5232..eeb88f6bcd2399a954df1e14a8e7c986898a33b5 100644
--- a/app/javascript/mastodon/components/missing_indicator.js
+++ b/app/javascript/mastodon/components/missing_indicator.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
 import illustration from 'mastodon/../images/elephant_ui_disappointed.svg';
diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.js
index b894aeaf989272dc35af0374ba1cfb9e697224b2..7a23e00055de94a2893a93bc5b1d62691e5ed07c 100644
--- a/app/javascript/mastodon/components/modal_root.js
+++ b/app/javascript/mastodon/components/modal_root.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import 'wicg-inert';
 import { createBrowserHistory } from 'history';
 import { multiply } from 'color-blend';
 
-export default class ModalRoot extends React.PureComponent {
+export default class ModalRoot extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
@@ -57,7 +57,7 @@ export default class ModalRoot extends React.PureComponent {
     this.history = this.context.router ? this.context.router.history : createBrowserHistory();
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (!!nextProps.children && !this.props.children) {
       this.activeElement = document.activeElement;
 
diff --git a/app/javascript/mastodon/components/permalink.js b/app/javascript/mastodon/components/permalink.js
index b369e98126d36d0d713dc49cb3c31cc44158dedd..285bf0831f75ed8aef1e7bf6ee487f08c0457f1d 100644
--- a/app/javascript/mastodon/components/permalink.js
+++ b/app/javascript/mastodon/components/permalink.js
@@ -1,7 +1,7 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 
-export default class Permalink extends React.PureComponent {
+export default class Permalink extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/components/picture_in_picture_placeholder.js b/app/javascript/mastodon/components/picture_in_picture_placeholder.js
index 19d15c18b16e379250a7193a7f04f2b0b444d2cf..ddb818951f84730fc2bc59668d3c63330130f68e 100644
--- a/app/javascript/mastodon/components/picture_in_picture_placeholder.js
+++ b/app/javascript/mastodon/components/picture_in_picture_placeholder.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import Icon from 'mastodon/components/icon';
 import { removePictureInPicture } from 'mastodon/actions/picture_in_picture';
@@ -7,7 +7,7 @@ import { debounce } from 'lodash';
 import { FormattedMessage } from 'react-intl';
 
 export default @connect()
-class PictureInPicturePlaceholder extends React.PureComponent {
+class PictureInPicturePlaceholder extends PureComponent {
 
   static propTypes = {
     width: PropTypes.number,
diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.js
index 85aa28816ca2bcecc2784bc2f3727e0986426ac2..cb0fe71313e6af29700c514ab29192c2f0965e98 100644
--- a/app/javascript/mastodon/components/poll.js
+++ b/app/javascript/mastodon/components/poll.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/components/radio_button.js b/app/javascript/mastodon/components/radio_button.js
index 0496fa2868ac7bfdfa294206cda3572362f163e8..00894e171c6347459aea829278753727f7b7a44c 100644
--- a/app/javascript/mastodon/components/radio_button.js
+++ b/app/javascript/mastodon/components/radio_button.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 
-export default class RadioButton extends React.PureComponent {
+export default class RadioButton extends PureComponent {
 
   static propTypes = {
     value: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/components/regeneration_indicator.js b/app/javascript/mastodon/components/regeneration_indicator.js
index 52696a4a724c63258a3fc652cc0f919beb183435..dbefc7a9b949176c4bd05320aa92ab2b94292d39 100644
--- a/app/javascript/mastodon/components/regeneration_indicator.js
+++ b/app/javascript/mastodon/components/regeneration_indicator.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { FormattedMessage } from 'react-intl';
 import illustration from 'mastodon/../images/elephant_ui_working.svg';
 
diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js
index 51248033927e5fb70462d4812c5cbef4dff815a6..5b5a998bb5180a375c4faf08c6665db98bd47efe 100644
--- a/app/javascript/mastodon/components/relative_timestamp.js
+++ b/app/javascript/mastodon/components/relative_timestamp.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { Component } from 'react';
 import { injectIntl, defineMessages } from 'react-intl';
 import PropTypes from 'prop-types';
 
@@ -122,7 +122,7 @@ const timeRemainingString = (intl, date, now, timeGiven = true) => {
 };
 
 export default @injectIntl
-class RelativeTimestamp extends React.Component {
+class RelativeTimestamp extends Component {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
@@ -149,7 +149,7 @@ class RelativeTimestamp extends React.Component {
       this.state.now !== nextState.now;
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (this.props.timestamp !== nextProps.timestamp) {
       this.setState({ now: this.props.intl.now() });
     }
@@ -159,7 +159,7 @@ class RelativeTimestamp extends React.Component {
     this._scheduleNextUpdate(this.props, this.state);
   }
 
-  componentWillUpdate (nextProps, nextState) {
+  UNSAFE_componentWillUpdate (nextProps, nextState) {
     this._scheduleNextUpdate(nextProps, nextState);
   }
 
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js
index 91d04bf4d8e933957929de7d5d88d4da77e4c67e..0d1ee7036c41964a914b460f02403c4744783dd4 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.js
@@ -1,4 +1,4 @@
-import React, { PureComponent } from 'react';
+import { Children, PureComponent, cloneElement } from 'react';
 import ScrollContainer from 'mastodon/containers/scroll_container';
 import PropTypes from 'prop-types';
 import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
@@ -182,8 +182,8 @@ class ScrollableList extends PureComponent {
   }
 
   getSnapshotBeforeUpdate (prevProps) {
-    const someItemInserted = React.Children.count(prevProps.children) > 0 &&
-      React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
+    const someItemInserted = Children.count(prevProps.children) > 0 &&
+      Children.count(prevProps.children) < Children.count(this.props.children) &&
       this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props);
     const pendingChanged = (prevProps.numPending > 0) !== (this.props.numPending > 0);
 
@@ -291,7 +291,7 @@ class ScrollableList extends PureComponent {
   render () {
     const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
     const { fullscreen } = this.state;
-    const childrenCount = React.Children.count(children);
+    const childrenCount = Children.count(children);
 
     const loadMore     = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
     const loadPending  = (numPending > 0) ? <LoadPending count={numPending} onClick={this.handleLoadPending} /> : null;
@@ -317,7 +317,7 @@ class ScrollableList extends PureComponent {
 
             {loadPending}
 
-            {React.Children.map(this.props.children, (child, index) => (
+            {Children.map(this.props.children, (child, index) => (
               <IntersectionObserverArticleContainer
                 key={child.key}
                 id={child.key}
@@ -326,7 +326,7 @@ class ScrollableList extends PureComponent {
                 intersectionObserverWrapper={this.intersectionObserverWrapper}
                 saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
               >
-                {React.cloneElement(child, {
+                {cloneElement(child, {
                   getScrollPosition: this.getScrollPosition,
                   updateScrollBottom: this.updateScrollBottom,
                   cachedMediaWidth: this.state.cachedMediaWidth,
diff --git a/app/javascript/mastodon/components/setting_text.js b/app/javascript/mastodon/components/setting_text.js
index a6dde4c0f103e0d90a3003009a1d24324fb65ee4..e0f3d42c36db38297642af83d7cbe7544765f4a0 100644
--- a/app/javascript/mastodon/components/setting_text.js
+++ b/app/javascript/mastodon/components/setting_text.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 
-export default class SettingText extends React.PureComponent {
+export default class SettingText extends PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/components/short_number.js b/app/javascript/mastodon/components/short_number.js
index 535c17727d5337523f041da3db13b4af60089d74..b9fecda6891d64678b1abd286f8b6b5e33e767da 100644
--- a/app/javascript/mastodon/components/short_number.js
+++ b/app/javascript/mastodon/components/short_number.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { memo } from 'react';
 import PropTypes from 'prop-types';
 import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
 import { FormattedMessage, FormattedNumber } from 'react-intl';
@@ -114,4 +114,4 @@ ShortNumberCounter.propTypes = {
   value: PropTypes.arrayOf(PropTypes.number),
 };
 
-export default React.memo(ShortNumber);
+export default memo(ShortNumber);
diff --git a/app/javascript/mastodon/components/skeleton.js b/app/javascript/mastodon/components/skeleton.js
index 09093e99c757ad2a9d6f56a5427611ea9f66cb50..72174f6bbe738e0e688b5a94e94c43c5b7dafc08 100644
--- a/app/javascript/mastodon/components/skeleton.js
+++ b/app/javascript/mastodon/components/skeleton.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 
 const Skeleton = ({ width, height }) => <span className='skeleton' style={{ width, height }}>&zwnj;</span>;
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 4ca3928242694d94ef9d315bd76dce46ccd02714..23ea0da186dac38a1ee710dd7e67ee0d916cef01 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from './avatar';
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index ab8755be04c4659b1bd63c7cc315950da02824e0..8d6783e74163e5f8b281eefd04e555df65089c17 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index 724165ada07a452d8b23ea67065191e896aca208..675a895f35cdaf36e5e37bd41106f94e02a02acc 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
@@ -10,7 +10,7 @@ import { autoPlayGif } from 'mastodon/initial_state';
 
 const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top)
 
-export default class StatusContent extends React.PureComponent {
+export default class StatusContent extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js
index 35e5749a33b8f434745674a094f15e0cba08f4a9..938aac898285e8c22ea6037c33ac21f36dd78654 100644
--- a/app/javascript/mastodon/components/status_list.js
+++ b/app/javascript/mastodon/components/status_list.js
@@ -1,5 +1,4 @@
 import { debounce } from 'lodash';
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import StatusContainer from '../containers/status_container';
diff --git a/app/javascript/mastodon/components/timeline_hint.js b/app/javascript/mastodon/components/timeline_hint.js
index fb55a62ccaee24ad0d5f4acd871717e6d8802bb0..fac08be0544ad15510d20328285385f425ee1378 100644
--- a/app/javascript/mastodon/components/timeline_hint.js
+++ b/app/javascript/mastodon/components/timeline_hint.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
 
diff --git a/app/javascript/mastodon/containers/account_container.js b/app/javascript/mastodon/containers/account_container.js
index 5a5136dd186d6fcded794be991a862e7e8c732cb..f3d3e039c7c26a67eb36988c388aeec24486d63e 100644
--- a/app/javascript/mastodon/containers/account_container.js
+++ b/app/javascript/mastodon/containers/account_container.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { makeGetAccount } from '../selectors';
diff --git a/app/javascript/mastodon/containers/admin_component.js b/app/javascript/mastodon/containers/admin_component.js
index 816b44bd17962b376e2c01c8e8801a9aaf54cd89..188485d40b47aa6b304731ad3f3fbfd477328700 100644
--- a/app/javascript/mastodon/containers/admin_component.js
+++ b/app/javascript/mastodon/containers/admin_component.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { IntlProvider, addLocaleData } from 'react-intl';
 import { getLocale } from '../locales';
@@ -6,7 +6,7 @@ import { getLocale } from '../locales';
 const { localeData, messages } = getLocale();
 addLocaleData(localeData);
 
-export default class AdminComponent extends React.PureComponent {
+export default class AdminComponent extends PureComponent {
 
   static propTypes = {
     locale: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/containers/compose_container.js b/app/javascript/mastodon/containers/compose_container.js
index 7bc7bbaa4dc29d97fec3bd9e124c2ea312221a2f..b0bc1d737874024f1377b1fae5d5afebacc13b70 100644
--- a/app/javascript/mastodon/containers/compose_container.js
+++ b/app/javascript/mastodon/containers/compose_container.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { Provider } from 'react-redux';
 import PropTypes from 'prop-types';
 import configureStore from '../store/configureStore';
@@ -20,7 +20,7 @@ if (initialState) {
 
 store.dispatch(fetchCustomEmojis());
 
-export default class TimelineContainer extends React.PureComponent {
+export default class TimelineContainer extends PureComponent {
 
   static propTypes = {
     locale: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/containers/domain_container.js b/app/javascript/mastodon/containers/domain_container.js
index 8a8ba1df12e6124c2f5b84974e358a98d5a00c3a..806bb81d10fea460d29578e91e09b55da10da90d 100644
--- a/app/javascript/mastodon/containers/domain_container.js
+++ b/app/javascript/mastodon/containers/domain_container.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { blockDomain, unblockDomain } from '../actions/domain_blocks';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js
index 0c3f6afa855c62cbc92eb135d43863cd351c94bb..dd0ab74174d1e512fb21a4879b63bc190a3ad125 100644
--- a/app/javascript/mastodon/containers/mastodon.js
+++ b/app/javascript/mastodon/containers/mastodon.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { Provider } from 'react-redux';
 import PropTypes from 'prop-types';
 import configureStore from '../store/configureStore';
@@ -28,7 +28,7 @@ const createIdentityContext = state => ({
   accessToken: state.meta.access_token,
 });
 
-export default class Mastodon extends React.PureComponent {
+export default class Mastodon extends PureComponent {
 
   static propTypes = {
     locale: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/containers/media_container.js b/app/javascript/mastodon/containers/media_container.js
index 6ee1f0bd8850daa1c05d7bda425927c3c57327d5..393886c587811c6b19b04118047eff42a4199d79 100644
--- a/app/javascript/mastodon/containers/media_container.js
+++ b/app/javascript/mastodon/containers/media_container.js
@@ -1,5 +1,5 @@
-import React, { PureComponent, Fragment } from 'react';
-import ReactDOM from 'react-dom';
+import { PureComponent } from 'react';
+import { createPortal } from 'react-dom';
 import PropTypes from 'prop-types';
 import { IntlProvider, addLocaleData } from 'react-intl';
 import { fromJS } from 'immutable';
@@ -74,7 +74,7 @@ export default class MediaContainer extends PureComponent {
 
     return (
       <IntlProvider locale={locale} messages={messages}>
-        <Fragment>
+        <>
           {[].map.call(components, (component, i) => {
             const componentName = component.getAttribute('data-component');
             const Component = MEDIA_COMPONENTS[componentName];
@@ -94,7 +94,7 @@ export default class MediaContainer extends PureComponent {
               }),
             });
 
-            return ReactDOM.createPortal(
+            return createPortal(
               <Component {...props} key={`media-${i}`} />,
               component,
             );
@@ -113,7 +113,7 @@ export default class MediaContainer extends PureComponent {
               />
             )}
           </ModalRoot>
-        </Fragment>
+        </>
       </IntlProvider>
     );
   }
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js
index ef0aca13a6d9472e0d2b5b4fc3090a3dfff8f40f..3a24c23ccff56607c3025f86241fbdcbdbb4a017 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import Status from '../components/status';
 import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
diff --git a/app/javascript/mastodon/containers/timeline_container.js b/app/javascript/mastodon/containers/timeline_container.js
index ed8095f90e23a5bef044e1d734d4f900ca8872d6..eefb5f0f22b542e271b6385341f8906a534f9a0d 100644
--- a/app/javascript/mastodon/containers/timeline_container.js
+++ b/app/javascript/mastodon/containers/timeline_container.js
@@ -1,5 +1,5 @@
-import React, { Fragment } from 'react';
-import ReactDOM from 'react-dom';
+import { PureComponent } from 'react';
+import { createPortal } from 'react-dom';
 import { Provider } from 'react-redux';
 import PropTypes from 'prop-types';
 import configureStore from '../store/configureStore';
@@ -20,7 +20,7 @@ if (initialState) {
   store.dispatch(hydrateStore(initialState));
 }
 
-export default class TimelineContainer extends React.PureComponent {
+export default class TimelineContainer extends PureComponent {
 
   static propTypes = {
     locale: PropTypes.string.isRequired,
@@ -46,14 +46,14 @@ export default class TimelineContainer extends React.PureComponent {
     return (
       <IntlProvider locale={locale} messages={messages}>
         <Provider store={store}>
-          <Fragment>
+          <>
             {timeline}
 
-            {ReactDOM.createPortal(
+            {createPortal(
               <ModalContainer />,
               document.getElementById('modal-container'),
             )}
-          </Fragment>
+          </>
         </Provider>
       </IntlProvider>
     );
diff --git a/app/javascript/mastodon/features/account/components/account_note.js b/app/javascript/mastodon/features/account/components/account_note.js
index 1787ce1abc34efbbc3f7122b6a3887e47b992a37..81dfd50face9a8a4bf1223acd0b673a8a3e05885 100644
--- a/app/javascript/mastodon/features/account/components/account_note.js
+++ b/app/javascript/mastodon/features/account/components/account_note.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@@ -10,7 +10,7 @@ const messages = defineMessages({
   placeholder: { id: 'account_note.placeholder', defaultMessage: 'Click to add a note' },
 });
 
-class InlineAlert extends React.PureComponent {
+class InlineAlert extends PureComponent {
 
   static propTypes = {
     show: PropTypes.bool,
@@ -22,7 +22,7 @@ class InlineAlert extends React.PureComponent {
 
   static TRANSITION_DELAY = 200;
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (!this.props.show && nextProps.show) {
       this.setState({ mountMessage: true });
     } else if (this.props.show && !nextProps.show) {
@@ -59,11 +59,11 @@ class AccountNote extends ImmutablePureComponent {
     saved: false,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this._reset();
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     const accountWillChange = !is(this.props.account, nextProps.account);
     const newState = {};
 
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js
index 8e6b9f0634ea4c84b886406cd101d8af72e60e86..baba32911189daf99f3961a79ecf2e9dcc203293 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@@ -282,10 +281,10 @@ class Header extends ImmutablePureComponent {
             {!suspended && (
               <div className='account__header__tabs__buttons'>
                 {!hidden && (
-                  <React.Fragment>
+                  <>
                     {actionBtn}
                     {bellBtn}
-                  </React.Fragment>
+                  </>
                 )}
 
                 <DropdownMenuContainer items={menu} icon='ellipsis-v' size={24} direction='right' />
diff --git a/app/javascript/mastodon/features/account_gallery/components/media_item.js b/app/javascript/mastodon/features/account_gallery/components/media_item.js
index ba7ec46a3307c313658c09e117babe10f8da922f..f4cc91970a2ee73ae1dee66cebec47c36f34bf64 100644
--- a/app/javascript/mastodon/features/account_gallery/components/media_item.js
+++ b/app/javascript/mastodon/features/account_gallery/components/media_item.js
@@ -4,7 +4,6 @@ import Icon from 'mastodon/components/icon';
 import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state';
 import { isIOS } from 'mastodon/is_mobile';
 import PropTypes from 'prop-types';
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 
diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js
index cc0bfa9baa359e27383b7b970c214429d8487bc5..2d319652177ff2fa8c24c30a7142091cad0f23a4 100644
--- a/app/javascript/mastodon/features/account_gallery/index.js
+++ b/app/javascript/mastodon/features/account_gallery/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js
index fab0bc597fb795f6ce9c97966e74f0602207d728..31312e4eeea8461330ed4b318a08e4b11f263b87 100644
--- a/app/javascript/mastodon/features/account_timeline/components/header.js
+++ b/app/javascript/mastodon/features/account_timeline/components/header.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import InnerHeader from '../../account/components/header';
diff --git a/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.js b/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.js
index 6b025596ca566f8c8ba3f4ae9c6793d402636213..192333dc356d38cc73e4624c58b8ac2186242ec8 100644
--- a/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.js
+++ b/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import { revealAccount } from 'mastodon/actions/accounts';
@@ -14,7 +14,7 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
 });
 
 export default @connect(() => {}, mapDispatchToProps)
-class LimitedAccountHint extends React.PureComponent {
+class LimitedAccountHint extends PureComponent {
 
   static propTypes = {
     accountId: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/account_timeline/components/moved_note.js b/app/javascript/mastodon/features/account_timeline/components/moved_note.js
index 2e32d660f849979c9a31c9be55ae8630d5230050..48afc9e41d4f41b1d639a9ca2cba24647471cc06 100644
--- a/app/javascript/mastodon/features/account_timeline/components/moved_note.js
+++ b/app/javascript/mastodon/features/account_timeline/components/moved_note.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage } from 'react-intl';
diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
index 371794dd7582686273bc836693f1eef4460a4539..07dc502ecd99284cc17be8eebe10b2a1f870df1d 100644
--- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js
+++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { makeGetAccount, getAccountHidden } from '../../../selectors';
 import Header from '../components/header';
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index 5b592c5a76ceea9198f2cec1e0ab45d1af1ce977..11599f24f54027a81fe8273f4148efc261c3b25a 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/audio/index.js b/app/javascript/mastodon/features/audio/index.js
index c47f55dd1ea8a42b1cbadec17c3062c585d9a5ce..b12de2ed3b2483738b92527b8af4a4957c1838b4 100644
--- a/app/javascript/mastodon/features/audio/index.js
+++ b/app/javascript/mastodon/features/audio/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
 import { formatTime } from 'mastodon/features/video';
@@ -21,7 +21,7 @@ const TICK_SIZE = 10;
 const PADDING   = 180;
 
 export default @injectIntl
-class Audio extends React.PureComponent {
+class Audio extends PureComponent {
 
   static propTypes = {
     src: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/blocks/index.js b/app/javascript/mastodon/features/blocks/index.js
index e00f2b60e0f3c4538acd6942c438d09355dde8c8..1e87a42c6bfd3dff0f649004f154a68552b0ef60 100644
--- a/app/javascript/mastodon/features/blocks/index.js
+++ b/app/javascript/mastodon/features/blocks/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
@@ -36,7 +35,7 @@ class Blocks extends ImmutablePureComponent {
     multiColumn: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchBlocks());
   }
 
diff --git a/app/javascript/mastodon/features/bookmarked_statuses/index.js b/app/javascript/mastodon/features/bookmarked_statuses/index.js
index 8f41b0f9514fd5c409c7f2169cd53f769d9223f3..8e7f9724c7055228460c2472f8839d90a90fe060 100644
--- a/app/javascript/mastodon/features/bookmarked_statuses/index.js
+++ b/app/javascript/mastodon/features/bookmarked_statuses/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -35,7 +34,7 @@ class Bookmarks extends ImmutablePureComponent {
     isLoading: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchBookmarkedStatuses());
   }
 
diff --git a/app/javascript/mastodon/features/community_timeline/components/column_settings.js b/app/javascript/mastodon/features/community_timeline/components/column_settings.js
index 0cb6db8836b9bda09fcd78e3f89c80b789170322..03ce451e4dc4c8e6cbda6f9dc5543114e8c3194f 100644
--- a/app/javascript/mastodon/features/community_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/community_timeline/components/column_settings.js
@@ -1,11 +1,11 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
 export default @injectIntl
-class ColumnSettings extends React.PureComponent {
+class ColumnSettings extends PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/community_timeline/index.js b/app/javascript/mastodon/features/community_timeline/index.js
index 30f77604867c251c924cb1ffe6e1231314cc46cd..aaa622d6d011ec4997b58061476e3f3696a06cca 100644
--- a/app/javascript/mastodon/features/community_timeline/index.js
+++ b/app/javascript/mastodon/features/community_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
@@ -29,7 +29,7 @@ const mapStateToProps = (state, { columnId }) => {
 
 export default @connect(mapStateToProps)
 @injectIntl
-class CommunityTimeline extends React.PureComponent {
+class CommunityTimeline extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/compose/components/action_bar.js b/app/javascript/mastodon/features/compose/components/action_bar.js
index 4ff0b7b94b6120d46c7dd36c96a6077df6e53bcc..836296fe5f9387a578dd9f970e70de7058401645 100644
--- a/app/javascript/mastodon/features/compose/components/action_bar.js
+++ b/app/javascript/mastodon/features/compose/components/action_bar.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
@@ -20,7 +20,7 @@ const messages = defineMessages({
 });
 
 export default @injectIntl
-class ActionBar extends React.PureComponent {
+class ActionBar extends PureComponent {
 
   static propTypes = {
     account: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/autosuggest_account.js b/app/javascript/mastodon/features/compose/components/autosuggest_account.js
index 1451be0e6467634f4515fcc1aeca3d8d61bf82de..30afeb0e5ec28feae43c97b3db4e951437498496 100644
--- a/app/javascript/mastodon/features/compose/components/autosuggest_account.js
+++ b/app/javascript/mastodon/features/compose/components/autosuggest_account.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import Avatar from '../../../components/avatar';
 import DisplayName from '../../../components/display_name';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/javascript/mastodon/features/compose/components/character_counter.js b/app/javascript/mastodon/features/compose/components/character_counter.js
index 0ecfc9141d8f641e25197f42d431c516b70377b8..15fc77bdcd2fe65a9143be53adf4a9870385fd2b 100644
--- a/app/javascript/mastodon/features/compose/components/character_counter.js
+++ b/app/javascript/mastodon/features/compose/components/character_counter.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { length } from 'stringz';
 
-export default class CharacterCounter extends React.PureComponent {
+export default class CharacterCounter extends PureComponent {
 
   static propTypes = {
     text: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js
index cd10d0edabef742469ccf4623b3c612c5319b19b..96ac744fbf23dae17eded54def564b93179c6bff 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.js
+++ b/app/javascript/mastodon/features/compose/components/compose_form.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import CharacterCounter from './character_counter';
 import Button from '../../../components/button';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
index f433e4de9faa6a7b0787a5a7605c5242b77e9a42..5aa5165c191b60a7accc212c0f023475c5a1aeb9 100644
--- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
@@ -47,7 +47,7 @@ const notFoundFn = () => (
   </div>
 );
 
-class ModifierPickerMenu extends React.PureComponent {
+class ModifierPickerMenu extends PureComponent {
 
   static propTypes = {
     active: PropTypes.bool,
@@ -59,7 +59,7 @@ class ModifierPickerMenu extends React.PureComponent {
     this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1);
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (nextProps.active) {
       this.attachListeners();
     } else {
@@ -108,7 +108,7 @@ class ModifierPickerMenu extends React.PureComponent {
 
 }
 
-class ModifierPicker extends React.PureComponent {
+class ModifierPicker extends PureComponent {
 
   static propTypes = {
     active: PropTypes.bool,
@@ -145,7 +145,7 @@ class ModifierPicker extends React.PureComponent {
 }
 
 @injectIntl
-class EmojiPickerMenu extends React.PureComponent {
+class EmojiPickerMenu extends PureComponent {
 
   static propTypes = {
     custom_emojis: ImmutablePropTypes.list,
@@ -309,7 +309,7 @@ class EmojiPickerMenu extends React.PureComponent {
 }
 
 export default @injectIntl
-class EmojiPickerDropdown extends React.PureComponent {
+class EmojiPickerDropdown extends PureComponent {
 
   static propTypes = {
     custom_emojis: ImmutablePropTypes.list,
diff --git a/app/javascript/mastodon/features/compose/components/language_dropdown.js b/app/javascript/mastodon/features/compose/components/language_dropdown.js
index d76490c775f89b6b5e704105a4f2811680a3a562..084980de8ce764870755298a082746c57391cad4 100644
--- a/app/javascript/mastodon/features/compose/components/language_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/language_dropdown.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { injectIntl, defineMessages } from 'react-intl';
 import TextIconButton from './text_icon_button';
@@ -34,7 +34,7 @@ const icons = {
 
 const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
 
-class LanguageDropdownMenu extends React.PureComponent {
+class LanguageDropdownMenu extends PureComponent {
 
   static propTypes = {
     style: PropTypes.object,
@@ -257,7 +257,7 @@ class LanguageDropdownMenu extends React.PureComponent {
 }
 
 export default @injectIntl
-class LanguageDropdown extends React.PureComponent {
+class LanguageDropdown extends PureComponent {
 
   static propTypes = {
     value: PropTypes.string,
diff --git a/app/javascript/mastodon/features/compose/components/navigation_bar.js b/app/javascript/mastodon/features/compose/components/navigation_bar.js
index e6ba7d8b73d15fcd9fe95aa302c116deccc01466..684e00a340e2e97116b8f8a51a371ef8a8829332 100644
--- a/app/javascript/mastodon/features/compose/components/navigation_bar.js
+++ b/app/javascript/mastodon/features/compose/components/navigation_bar.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ActionBar from './action_bar';
diff --git a/app/javascript/mastodon/features/compose/components/poll_button.js b/app/javascript/mastodon/features/compose/components/poll_button.js
index 76f96bfa41ef0e3dae4e990c24e78f01ac9529b3..22192778442e8e7de479e99644d2c222b28e3063 100644
--- a/app/javascript/mastodon/features/compose/components/poll_button.js
+++ b/app/javascript/mastodon/features/compose/components/poll_button.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import IconButton from '../../../components/icon_button';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
@@ -15,7 +15,7 @@ const iconStyle = {
 
 export default
 @injectIntl
-class PollButton extends React.PureComponent {
+class PollButton extends PureComponent {
 
   static propTypes = {
     disabled: PropTypes.bool,
diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.js
index db49f90eb4cb957524101bd840d2e989c4747784..59891b78837fcf7c359c99162b6d2c86924f52c2 100644
--- a/app/javascript/mastodon/features/compose/components/poll_form.js
+++ b/app/javascript/mastodon/features/compose/components/poll_form.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
@@ -21,7 +21,7 @@ const messages = defineMessages({
 });
 
 @injectIntl
-class Option extends React.PureComponent {
+class Option extends PureComponent {
 
   static propTypes = {
     title: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
index 1f0e998d36f89be17dceabeed7012f9076948a3c..dc7ef674d83d0e9f551b24e6dbc0e6af28ffdccd 100644
--- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { injectIntl, defineMessages } from 'react-intl';
 import IconButton from '../../../components/icon_button';
@@ -23,7 +23,7 @@ const messages = defineMessages({
 
 const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
 
-class PrivacyDropdownMenu extends React.PureComponent {
+class PrivacyDropdownMenu extends PureComponent {
 
   static propTypes = {
     style: PropTypes.object,
@@ -149,7 +149,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
 }
 
 export default @injectIntl
-class PrivacyDropdown extends React.PureComponent {
+class PrivacyDropdown extends PureComponent {
 
   static propTypes = {
     isUserTouching: PropTypes.func,
@@ -231,7 +231,7 @@ class PrivacyDropdown extends React.PureComponent {
     this.props.onChange(value);
   }
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     const { intl: { formatMessage } } = this.props;
 
     this.options = [
diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.js b/app/javascript/mastodon/features/compose/components/reply_indicator.js
index 863defb768f416ef7fde6e85445428f02f4be6cf..051de00be71015d8b59f909c8b6d6fd9620c48aa 100644
--- a/app/javascript/mastodon/features/compose/components/reply_indicator.js
+++ b/app/javascript/mastodon/features/compose/components/reply_indicator.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from '../../../components/avatar';
diff --git a/app/javascript/mastodon/features/compose/components/search.js b/app/javascript/mastodon/features/compose/components/search.js
index 3e36a922be674fe738746941d38b4b7d9ce0d6de..383781bf5be67bbfacd03a79adf39b8302fd07ae 100644
--- a/app/javascript/mastodon/features/compose/components/search.js
+++ b/app/javascript/mastodon/features/compose/components/search.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import Overlay from 'react-overlays/lib/Overlay';
@@ -11,7 +11,7 @@ const messages = defineMessages({
   placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
 });
 
-class SearchPopout extends React.PureComponent {
+class SearchPopout extends PureComponent {
 
   static propTypes = {
     style: PropTypes.object,
@@ -45,7 +45,7 @@ class SearchPopout extends React.PureComponent {
 }
 
 export default @injectIntl
-class Search extends React.PureComponent {
+class Search extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/search_results.js b/app/javascript/mastodon/features/compose/components/search_results.js
index e2493a6c6ab8fb909799f84ce35ef83e3d118bb9..81289025db8d837f13e04fecb82e0652feab8eb7 100644
--- a/app/javascript/mastodon/features/compose/components/search_results.js
+++ b/app/javascript/mastodon/features/compose/components/search_results.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
diff --git a/app/javascript/mastodon/features/compose/components/text_icon_button.js b/app/javascript/mastodon/features/compose/components/text_icon_button.js
index cd644b680e9501d48c5cd742ff6f96eaa6d4a4e3..6350001eee87dcb8dea196d4ea54e56753e602fe 100644
--- a/app/javascript/mastodon/features/compose/components/text_icon_button.js
+++ b/app/javascript/mastodon/features/compose/components/text_icon_button.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 
 const iconStyle = {
@@ -7,7 +7,7 @@ const iconStyle = {
   width: `${18 * 1.28571429}px`,
 };
 
-export default class TextIconButton extends React.PureComponent {
+export default class TextIconButton extends PureComponent {
 
   static propTypes = {
     label: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js
index 706824dc712e83dc36296a2cd2cb3d8a5f9dc4f8..c9a934398d2f90481ce6690b8f47741f9e805ee2 100644
--- a/app/javascript/mastodon/features/compose/components/upload.js
+++ b/app/javascript/mastodon/features/compose/components/upload.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Motion from '../../ui/util/optional_motion';
diff --git a/app/javascript/mastodon/features/compose/components/upload_button.js b/app/javascript/mastodon/features/compose/components/upload_button.js
index 9cb36167a86893b7c761a3714b2955e42afa9e62..e2756fcf86b41188dec5ce569732cc6363b55028 100644
--- a/app/javascript/mastodon/features/compose/components/upload_button.js
+++ b/app/javascript/mastodon/features/compose/components/upload_button.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import IconButton from '../../../components/icon_button';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
diff --git a/app/javascript/mastodon/features/compose/components/upload_form.js b/app/javascript/mastodon/features/compose/components/upload_form.js
index c6eac554e6cf6fbe7b13ee4631cf7fd82f3bf56a..b07bdbbd64bfe3edd7727e89982247750ed09657 100644
--- a/app/javascript/mastodon/features/compose/components/upload_form.js
+++ b/app/javascript/mastodon/features/compose/components/upload_form.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import UploadProgressContainer from '../containers/upload_progress_container';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/compose/components/upload_progress.js b/app/javascript/mastodon/features/compose/components/upload_progress.js
index b0bfe0c9a142bb991bcb5558c4c82c66d1ab23c6..5d92e6ed2819e2f0b3c46e74ac247724434e43cf 100644
--- a/app/javascript/mastodon/features/compose/components/upload_progress.js
+++ b/app/javascript/mastodon/features/compose/components/upload_progress.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import Motion from '../../ui/util/optional_motion';
 import spring from 'react-motion/lib/spring';
 import Icon from 'mastodon/components/icon';
 
-export default class UploadProgress extends React.PureComponent {
+export default class UploadProgress extends PureComponent {
 
   static propTypes = {
     active: PropTypes.bool,
diff --git a/app/javascript/mastodon/features/compose/components/warning.js b/app/javascript/mastodon/features/compose/components/warning.js
index 803b7f86abd2d949a63ee502a7983d237a4dd17e..4947a4e28b996e6c0793eccbc395e8465e6d6b2f 100644
--- a/app/javascript/mastodon/features/compose/components/warning.js
+++ b/app/javascript/mastodon/features/compose/components/warning.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import Motion from '../../ui/util/optional_motion';
 import spring from 'react-motion/lib/spring';
 
-export default class Warning extends React.PureComponent {
+export default class Warning extends PureComponent {
 
   static propTypes = {
     message: PropTypes.node.isRequired,
diff --git a/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
index 1bcce57310a86a7422f1bfd7641bd355874ec028..2a99b2620cf34779341bb29279af453766fd16d1 100644
--- a/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
+++ b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
@@ -30,7 +30,7 @@ const mapDispatchToProps = dispatch => ({
 
 });
 
-class SensitiveButton extends React.PureComponent {
+class SensitiveButton extends PureComponent {
 
   static propTypes = {
     active: PropTypes.bool,
diff --git a/app/javascript/mastodon/features/compose/containers/warning_container.js b/app/javascript/mastodon/features/compose/containers/warning_container.js
index 571d1d8382667d00ffcd54617aca4a4d35da3703..76653f7abe8a88e05b40e3c72e9b6f84ada5c862 100644
--- a/app/javascript/mastodon/features/compose/containers/warning_container.js
+++ b/app/javascript/mastodon/features/compose/containers/warning_container.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import Warning from '../components/warning';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js
index 711a5590460c28da44a507270684b205a340116d..c11043a45d53d0aadfe37f5152049fa46d419638 100644
--- a/app/javascript/mastodon/features/compose/index.js
+++ b/app/javascript/mastodon/features/compose/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ComposeFormContainer from './containers/compose_form_container';
 import NavigationContainer from './containers/navigation_container';
 import PropTypes from 'prop-types';
@@ -38,7 +38,7 @@ const mapStateToProps = (state, ownProps) => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class Compose extends React.PureComponent {
+class Compose extends PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversation.js b/app/javascript/mastodon/features/direct_timeline/components/conversation.js
index 77ff2ce7b3b3df5479b0e6f8dcc947038d70a4af..6d6d8c3c6cfa6feb853157b3ea8551e509c6ec46 100644
--- a/app/javascript/mastodon/features/direct_timeline/components/conversation.js
+++ b/app/javascript/mastodon/features/direct_timeline/components/conversation.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js b/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js
index fd1df7256896dcdfa5090774a10795e6c6772857..554268285849033a70b6ab889544ab91b8e31873 100644
--- a/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js
+++ b/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js
index debb2d721c8ab91aa79222146b661649a34a8385..1686ecccd2d7ac21c66a7f59fb91f79d5cbc9373 100644
--- a/app/javascript/mastodon/features/direct_timeline/index.js
+++ b/app/javascript/mastodon/features/direct_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import Column from '../../components/column';
@@ -15,7 +15,7 @@ const messages = defineMessages({
 
 export default @connect()
 @injectIntl
-class DirectTimeline extends React.PureComponent {
+class DirectTimeline extends PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/directory/components/account_card.js b/app/javascript/mastodon/features/directory/components/account_card.js
index 27ba4e7f4050fed49a14adba5e10ee1dd6f697d3..87a664d552ec2ab56e4f5dc1d0feb664cf6219f3 100644
--- a/app/javascript/mastodon/features/directory/components/account_card.js
+++ b/app/javascript/mastodon/features/directory/components/account_card.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/directory/index.js b/app/javascript/mastodon/features/directory/index.js
index 94d7d1a9c544751bec7733e29a191b36ef9422e4..9e8090dfdf056e59d8fbc24868506ff35d924ea9 100644
--- a/app/javascript/mastodon/features/directory/index.js
+++ b/app/javascript/mastodon/features/directory/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl } from 'react-intl';
 import PropTypes from 'prop-types';
@@ -30,7 +30,7 @@ const mapStateToProps = state => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class Directory extends React.PureComponent {
+class Directory extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js
index edb80aef4100f2b07f7a4de8c56beeb21762c681..03c9b31b680452599072edc39ac90bd6501a6239 100644
--- a/app/javascript/mastodon/features/domain_blocks/index.js
+++ b/app/javascript/mastodon/features/domain_blocks/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
@@ -35,7 +34,7 @@ class Blocks extends ImmutablePureComponent {
     multiColumn: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchDomainBlocks());
   }
 
diff --git a/app/javascript/mastodon/features/explore/components/story.js b/app/javascript/mastodon/features/explore/components/story.js
index 5631280297046e9f06df48e7f5d2d892b27cc21d..977d9eea80f802bf190b82c5f055da4562872900 100644
--- a/app/javascript/mastodon/features/explore/components/story.js
+++ b/app/javascript/mastodon/features/explore/components/story.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import Blurhash from 'mastodon/components/blurhash';
 import { accountsCountRenderer } from 'mastodon/components/hashtag';
@@ -6,7 +6,7 @@ import ShortNumber from 'mastodon/components/short_number';
 import Skeleton from 'mastodon/components/skeleton';
 import classNames from 'classnames';
 
-export default class Story extends React.PureComponent {
+export default class Story extends PureComponent {
 
   static propTypes = {
     url: PropTypes.string,
@@ -38,10 +38,10 @@ export default class Story extends React.PureComponent {
 
         <div className='story__thumbnail'>
           {thumbnail ? (
-            <React.Fragment>
+            <>
               <div className={classNames('story__thumbnail__preview', { 'story__thumbnail__preview--hidden': thumbnailLoaded })}><Blurhash hash={blurhash} /></div>
               <img src={thumbnail} onLoad={this.handleImageLoad} alt='' role='presentation' />
-            </React.Fragment>
+            </>
           ) : <Skeleton />}
         </div>
       </a>
diff --git a/app/javascript/mastodon/features/explore/index.js b/app/javascript/mastodon/features/explore/index.js
index 8082f2d99cb54af7f020a608f6c8e43390a4c89f..18275f868bb6475fcb8a59aeaff16463978493f6 100644
--- a/app/javascript/mastodon/features/explore/index.js
+++ b/app/javascript/mastodon/features/explore/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
@@ -24,7 +24,7 @@ const mapStateToProps = state => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class Explore extends React.PureComponent {
+class Explore extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
@@ -67,7 +67,7 @@ class Explore extends React.PureComponent {
           {isSearching ? (
             <SearchResults />
           ) : (
-            <React.Fragment>
+            <>
               <div className='account__section-headline'>
                 <NavLink exact to='/explore'><FormattedMessage id='explore.trending_statuses' defaultMessage='Posts' /></NavLink>
                 <NavLink exact to='/explore/tags'><FormattedMessage id='explore.trending_tags' defaultMessage='Hashtags' /></NavLink>
@@ -81,7 +81,7 @@ class Explore extends React.PureComponent {
                 <Route path='/explore/suggestions' component={Suggestions} />
                 <Route exact path={['/explore', '/explore/posts', '/search']} component={Statuses} componentParams={{ multiColumn }} />
               </Switch>
-            </React.Fragment>
+            </>
           )}
         </div>
       </Column>
diff --git a/app/javascript/mastodon/features/explore/links.js b/app/javascript/mastodon/features/explore/links.js
index 6649fb6e47143a083a2de714735a42574d0bf189..c7fc2c33311fb8c813c038289b0876290f53777d 100644
--- a/app/javascript/mastodon/features/explore/links.js
+++ b/app/javascript/mastodon/features/explore/links.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Story from './components/story';
@@ -12,7 +12,7 @@ const mapStateToProps = state => ({
 });
 
 export default @connect(mapStateToProps)
-class Links extends React.PureComponent {
+class Links extends PureComponent {
 
   static propTypes = {
     links: ImmutablePropTypes.list,
diff --git a/app/javascript/mastodon/features/explore/results.js b/app/javascript/mastodon/features/explore/results.js
index 1286020f54c04f09610dad8d5658f5ae20462a5f..1a784367095474adc5a586aabffb910dc2c7d4e0 100644
--- a/app/javascript/mastodon/features/explore/results.js
+++ b/app/javascript/mastodon/features/explore/results.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage } from 'react-intl';
@@ -37,7 +37,7 @@ const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', resul
 )), onLoadMore);
 
 export default @connect(mapStateToProps)
-class Results extends React.PureComponent {
+class Results extends PureComponent {
 
   static propTypes = {
     results: ImmutablePropTypes.map,
@@ -95,7 +95,7 @@ class Results extends React.PureComponent {
     }
 
     return (
-      <React.Fragment>
+      <>
         <div className='account__section-headline'>
           <button onClick={this.handleSelectAll} className={type === 'all' && 'active'}><FormattedMessage id='search_results.all' defaultMessage='All' /></button>
           <button onClick={this.handleSelectAccounts} className={type === 'accounts' && 'active'}><FormattedMessage id='search_results.accounts' defaultMessage='People' /></button>
@@ -106,7 +106,7 @@ class Results extends React.PureComponent {
         <div className='explore__search-results'>
           {isLoading ? <LoadingIndicator /> : filteredResults}
         </div>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/features/explore/statuses.js b/app/javascript/mastodon/features/explore/statuses.js
index 33e5b4179649f1034853ad1221c6c0dadf8a2198..023f31264a11ca1e80e136112218bdb6df0cf200 100644
--- a/app/javascript/mastodon/features/explore/statuses.js
+++ b/app/javascript/mastodon/features/explore/statuses.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import StatusList from 'mastodon/components/status_list';
@@ -14,7 +14,7 @@ const mapStateToProps = state => ({
 });
 
 export default @connect(mapStateToProps)
-class Statuses extends React.PureComponent {
+class Statuses extends PureComponent {
 
   static propTypes = {
     statusIds: ImmutablePropTypes.list,
diff --git a/app/javascript/mastodon/features/explore/suggestions.js b/app/javascript/mastodon/features/explore/suggestions.js
index 0c6a7ef8a3b791d04f4e176c50d26e62bc8fe7be..f8ea7e1f95844db4a3c9ec089e621dc85d9edd65 100644
--- a/app/javascript/mastodon/features/explore/suggestions.js
+++ b/app/javascript/mastodon/features/explore/suggestions.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import AccountCard from 'mastodon/features/directory/components/account_card';
@@ -12,7 +12,7 @@ const mapStateToProps = state => ({
 });
 
 export default @connect(mapStateToProps)
-class Suggestions extends React.PureComponent {
+class Suggestions extends PureComponent {
 
   static propTypes = {
     isLoading: PropTypes.bool,
diff --git a/app/javascript/mastodon/features/explore/tags.js b/app/javascript/mastodon/features/explore/tags.js
index c0ad9fc6ec928d38fb5e917e548632ace52e4efd..50f7e7f03a7d20460d221430524a8bdb9cbff13d 100644
--- a/app/javascript/mastodon/features/explore/tags.js
+++ b/app/javascript/mastodon/features/explore/tags.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
@@ -12,7 +12,7 @@ const mapStateToProps = state => ({
 });
 
 export default @connect(mapStateToProps)
-class Tags extends React.PureComponent {
+class Tags extends PureComponent {
 
   static propTypes = {
     hashtags: ImmutablePropTypes.list,
diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.js
index 73631946a7b6ab2115286bf4d98a3457ad76022d..db5157a565deeeef4b0ed9570534333c72908842 100644
--- a/app/javascript/mastodon/features/favourited_statuses/index.js
+++ b/app/javascript/mastodon/features/favourited_statuses/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -35,7 +34,7 @@ class Favourites extends ImmutablePureComponent {
     isLoading: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchFavouritedStatuses());
   }
 
diff --git a/app/javascript/mastodon/features/favourites/index.js b/app/javascript/mastodon/features/favourites/index.js
index f060068a49b385826561109cec90dbf35224ec2a..0452e285a97f3067c1daa6dcbeac44ae6566bb3e 100644
--- a/app/javascript/mastodon/features/favourites/index.js
+++ b/app/javascript/mastodon/features/favourites/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import PropTypes from 'prop-types';
@@ -32,13 +31,13 @@ class Favourites extends ImmutablePureComponent {
     intl: PropTypes.object.isRequired,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     if (!this.props.accountIds) {
       this.props.dispatch(fetchFavourites(this.props.params.statusId));
     }
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
       this.props.dispatch(fetchFavourites(nextProps.params.statusId));
     }
diff --git a/app/javascript/mastodon/features/follow_recommendations/components/account.js b/app/javascript/mastodon/features/follow_recommendations/components/account.js
index ffc0ab00cc21de88e4e22a0ce10cbaecfcb734d5..5beccef6d1a3f99a9391d2e613a4a7b3d0987720 100644
--- a/app/javascript/mastodon/features/follow_recommendations/components/account.js
+++ b/app/javascript/mastodon/features/follow_recommendations/components/account.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/follow_recommendations/index.js b/app/javascript/mastodon/features/follow_recommendations/index.js
index b5a71aef5b3fffffb49459dd1d31856d1786d51d..6b210dfd32216041989f5dfe2098fd056373bee4 100644
--- a/app/javascript/mastodon/features/follow_recommendations/index.js
+++ b/app/javascript/mastodon/features/follow_recommendations/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -84,7 +83,7 @@ class FollowRecommendations extends ImmutablePureComponent {
           </div>
 
           {!isLoading && (
-            <React.Fragment>
+            <>
               <div className='column-list'>
                 {suggestions.size > 0 ? suggestions.map(suggestion => (
                   <Account key={suggestion.get('account')} id={suggestion.get('account')} />
@@ -99,7 +98,7 @@ class FollowRecommendations extends ImmutablePureComponent {
                 <img src={imageGreeting} alt='' className='column-actions__background' />
                 <Button onClick={this.handleDone}><FormattedMessage id='follow_recommendations.done' defaultMessage='Done' /></Button>
               </div>
-            </React.Fragment>
+            </>
           )}
         </div>
       </Column>
diff --git a/app/javascript/mastodon/features/follow_requests/components/account_authorize.js b/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
index 263a7ae1626332a1e0eee19c49f2502d7bc4d30a..bb81cfb2e2f59699bd5923f48b86dea38bc19244 100644
--- a/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
+++ b/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Permalink from '../../../components/permalink';
diff --git a/app/javascript/mastodon/features/follow_requests/index.js b/app/javascript/mastodon/features/follow_requests/index.js
index 1f9b635bb781854293cb41aa1e4bf364837723a2..68396645bc8748a214d5d9cc39daa558df0bbefe 100644
--- a/app/javascript/mastodon/features/follow_requests/index.js
+++ b/app/javascript/mastodon/features/follow_requests/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
@@ -41,7 +40,7 @@ class FollowRequests extends ImmutablePureComponent {
     multiColumn: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchFollowRequests());
   }
 
diff --git a/app/javascript/mastodon/features/followers/index.js b/app/javascript/mastodon/features/followers/index.js
index 5b7f402f8d9f8d4e9f45e5e22c014527055e545f..d180b96dd6ee41053b2651479be5e7c62a4d1221 100644
--- a/app/javascript/mastodon/features/followers/index.js
+++ b/app/javascript/mastodon/features/followers/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/following/index.js b/app/javascript/mastodon/features/following/index.js
index 143082d760a0862cf409a6f061dda5816d8f4f1a..a400d79c8e4ea379a42aadac79f833e990035d28 100644
--- a/app/javascript/mastodon/features/following/index.js
+++ b/app/javascript/mastodon/features/following/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/generic_not_found/index.js b/app/javascript/mastodon/features/generic_not_found/index.js
index 41cd61a5f8b610f2b00260443dc372e01b2f7372..c7f82626e619315815959fa2560d588ab939ce38 100644
--- a/app/javascript/mastodon/features/generic_not_found/index.js
+++ b/app/javascript/mastodon/features/generic_not_found/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import Column from '../ui/components/column';
 import MissingIndicator from '../../components/missing_indicator';
 
diff --git a/app/javascript/mastodon/features/getting_started/components/announcements.js b/app/javascript/mastodon/features/getting_started/components/announcements.js
index 24db8cedec6073e6ec147196d892f4811441b64a..567ce4ba5fe94f0bb729a1849efce29bbc90af63 100644
--- a/app/javascript/mastodon/features/getting_started/components/announcements.js
+++ b/app/javascript/mastodon/features/getting_started/components/announcements.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import ReactSwipeableViews from 'react-swipeable-views';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -149,7 +149,7 @@ class Content extends ImmutablePureComponent {
 
 }
 
-class Emoji extends React.PureComponent {
+class Emoji extends PureComponent {
 
   static propTypes = {
     emoji: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/getting_started/components/trends.js b/app/javascript/mastodon/features/getting_started/components/trends.js
index 71c7c458dca01ff8a4eb8244cec23a61b5cd85ab..4ea812901deeee338cabc7ac0b3fddf99d5154ad 100644
--- a/app/javascript/mastodon/features/getting_started/components/trends.js
+++ b/app/javascript/mastodon/features/getting_started/components/trends.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js
index 65cee7498b764211683c4644ee440fc0e9e4a368..e58f7b63c775ac5748f0813d85c5080d4383e7bd 100644
--- a/app/javascript/mastodon/features/getting_started/index.js
+++ b/app/javascript/mastodon/features/getting_started/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import Column from '../ui/components/column';
 import ColumnLink from '../ui/components/column_link';
 import ColumnSubheading from '../ui/components/column_subheading';
diff --git a/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.js b/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.js
index ede8907e50bd6156b9267dcd2fd6ae79aa5f504e..c375d585ed6c913a885781d2eb7da08404838d4b 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@@ -13,7 +13,7 @@ const messages = defineMessages({
 });
 
 export default @injectIntl
-class ColumnSettings extends React.PureComponent {
+class ColumnSettings extends PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.js b/app/javascript/mastodon/features/hashtag_timeline/index.js
index 6a808eb306c4827358ed8bf4c2c1dace12d36e87..10b3358ce2f08c4d66084ccace74eb4543897493 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/index.js
+++ b/app/javascript/mastodon/features/hashtag_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import StatusListContainer from '../ui/containers/status_list_container';
@@ -16,7 +16,7 @@ const mapStateToProps = (state, props) => ({
 });
 
 export default @connect(mapStateToProps)
-class HashtagTimeline extends React.PureComponent {
+class HashtagTimeline extends PureComponent {
 
   disconnects = [];
 
@@ -103,7 +103,7 @@ class HashtagTimeline extends React.PureComponent {
     dispatch(expandHashtagTimeline(id, { tags, local }));
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     const { dispatch, params } = this.props;
     const { id, tags, local } = nextProps.params;
 
diff --git a/app/javascript/mastodon/features/home_timeline/components/column_settings.js b/app/javascript/mastodon/features/home_timeline/components/column_settings.js
index 455e218817cfe717b0b484a980d405300fc05ab0..490ae99b457cb74aff06acddd4ee7787c950dd30 100644
--- a/app/javascript/mastodon/features/home_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/home_timeline/components/column_settings.js
@@ -1,11 +1,11 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
 export default @injectIntl
-class ColumnSettings extends React.PureComponent {
+class ColumnSettings extends PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.js
index dc440f2fe00a9f0081f60f57cb483844753f87d0..ff04ddfb384b7acdf7f1d7ed3545ef5ef9d8f1df 100644
--- a/app/javascript/mastodon/features/home_timeline/index.js
+++ b/app/javascript/mastodon/features/home_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import { expandHomeTimeline } from '../../actions/timelines';
 import PropTypes from 'prop-types';
@@ -30,7 +30,7 @@ const mapStateToProps = state => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class HomeTimeline extends React.PureComponent {
+class HomeTimeline extends PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
index 8f1631d8295d6dd06cb52fe4c433c906845a1e19..3072c5d38c764915c675d7f389029ee612e93d03 100644
--- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js
+++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import Column from '../ui/components/column';
 import ColumnBackButtonSlim from '../../components/column_back_button_slim';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
diff --git a/app/javascript/mastodon/features/list_adder/components/account.js b/app/javascript/mastodon/features/list_adder/components/account.js
index 1369aac0742c437a48cc2f2bf69b0d2bc5d38c13..c3468a930e2b49e72e69e8d83ce9036ee96ca5ab 100644
--- a/app/javascript/mastodon/features/list_adder/components/account.js
+++ b/app/javascript/mastodon/features/list_adder/components/account.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { makeGetAccount } from '../../../selectors';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/list_adder/components/list.js b/app/javascript/mastodon/features/list_adder/components/list.js
index 60c8958a73762981bf3c1271f213d455f7f65543..88458856dc48291da5ef00950ac6f2e9d987995f 100644
--- a/app/javascript/mastodon/features/list_adder/components/list.js
+++ b/app/javascript/mastodon/features/list_adder/components/list.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/list_adder/index.js b/app/javascript/mastodon/features/list_adder/index.js
index cb8a15e8c7def8d12f008e462b53f882f18e7710..f3e341590058ec70b0f7a32b31bda11111cff805 100644
--- a/app/javascript/mastodon/features/list_adder/index.js
+++ b/app/javascript/mastodon/features/list_adder/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
diff --git a/app/javascript/mastodon/features/list_editor/components/account.js b/app/javascript/mastodon/features/list_editor/components/account.js
index 48085af43d1e7e4c89be728ed637fa56425cf38d..22b41ce81accad79eb7edbc6ad3216cf167b6400 100644
--- a/app/javascript/mastodon/features/list_editor/components/account.js
+++ b/app/javascript/mastodon/features/list_editor/components/account.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import { makeGetAccount } from '../../../selectors';
diff --git a/app/javascript/mastodon/features/list_editor/components/edit_list_form.js b/app/javascript/mastodon/features/list_editor/components/edit_list_form.js
index 3ccab12a800c7891ffde4ceeb3ba1f12b2d2cb6c..f092a5aa26e4df7a5c2a524829ef5b3f1cc4538c 100644
--- a/app/javascript/mastodon/features/list_editor/components/edit_list_form.js
+++ b/app/javascript/mastodon/features/list_editor/components/edit_list_form.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { changeListEditorTitle, submitListEditor } from '../../../actions/lists';
@@ -21,7 +21,7 @@ const mapDispatchToProps = dispatch => ({
 
 export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-class ListForm extends React.PureComponent {
+class ListForm extends PureComponent {
 
   static propTypes = {
     value: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/list_editor/components/search.js b/app/javascript/mastodon/features/list_editor/components/search.js
index e3f069bb8072ba83ebccbe0378d473668a28bc0d..721fffb021874687bd429014b710b68c2fc6a079 100644
--- a/app/javascript/mastodon/features/list_editor/components/search.js
+++ b/app/javascript/mastodon/features/list_editor/components/search.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl } from 'react-intl';
@@ -22,7 +22,7 @@ const mapDispatchToProps = dispatch => ({
 
 export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-class Search extends React.PureComponent {
+class Search extends PureComponent {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/list_editor/index.js b/app/javascript/mastodon/features/list_editor/index.js
index 48466604a7d2176e1649fdcc4a45726063826e99..0e68637f38ccfc1efbdcee6b55aed9a1ebf33bfe 100644
--- a/app/javascript/mastodon/features/list_editor/index.js
+++ b/app/javascript/mastodon/features/list_editor/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.js
index 8010274f88025465a59e9c144e9e3e742e7c08b9..91fbfeceef906f684119d2adb7a27f4fb06bacab 100644
--- a/app/javascript/mastodon/features/list_timeline/index.js
+++ b/app/javascript/mastodon/features/list_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -32,7 +32,7 @@ const mapStateToProps = (state, props) => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class ListTimeline extends React.PureComponent {
+class ListTimeline extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
@@ -78,7 +78,7 @@ class ListTimeline extends React.PureComponent {
     this.disconnect = dispatch(connectListStream(id));
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     const { dispatch } = this.props;
     const { id } = nextProps.params;
 
diff --git a/app/javascript/mastodon/features/lists/components/new_list_form.js b/app/javascript/mastodon/features/lists/components/new_list_form.js
index 7faf50be81c4842f3458faad47b6f3391591fa1b..acc2d8216e9544b643f7cd101c0ebfaff94296b9 100644
--- a/app/javascript/mastodon/features/lists/components/new_list_form.js
+++ b/app/javascript/mastodon/features/lists/components/new_list_form.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { changeListEditorTitle, submitListEditor } from '../../../actions/lists';
@@ -22,7 +22,7 @@ const mapDispatchToProps = dispatch => ({
 
 export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-class NewListForm extends React.PureComponent {
+class NewListForm extends PureComponent {
 
   static propTypes = {
     value: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/lists/index.js b/app/javascript/mastodon/features/lists/index.js
index 809d79d99810f93e4c2bc044756ecf27ee6a0423..de6c8dca1a6b2e8ec27e44f106732d6a97f3ce0b 100644
--- a/app/javascript/mastodon/features/lists/index.js
+++ b/app/javascript/mastodon/features/lists/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -43,7 +42,7 @@ class Lists extends ImmutablePureComponent {
     multiColumn: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchLists());
   }
 
diff --git a/app/javascript/mastodon/features/mutes/index.js b/app/javascript/mastodon/features/mutes/index.js
index c21433cc4144650a5bd848cbb07398869a75d4f6..75cd1d73d28b202a84acf6fbbf4287d727f6fb19 100644
--- a/app/javascript/mastodon/features/mutes/index.js
+++ b/app/javascript/mastodon/features/mutes/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
@@ -36,7 +35,7 @@ class Mutes extends ImmutablePureComponent {
     multiColumn: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchMutes());
   }
 
diff --git a/app/javascript/mastodon/features/notifications/components/clear_column_button.js b/app/javascript/mastodon/features/notifications/components/clear_column_button.js
index b82fd092f8ea076f0021d8bc3e284ffbf5f6cb99..3d73625fe2c50262632ac6d305ff671b918c35dc 100644
--- a/app/javascript/mastodon/features/notifications/components/clear_column_button.js
+++ b/app/javascript/mastodon/features/notifications/components/clear_column_button.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
 import Icon from 'mastodon/components/icon';
 
-export default class ClearColumnButton extends React.PureComponent {
+export default class ClearColumnButton extends PureComponent {
 
   static propTypes = {
     onClick: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.js b/app/javascript/mastodon/features/notifications/components/column_settings.js
index 61df79b46464cf757c325a7255ff026523884916..5a05a4790125cc9eaee11e732f303598a94b74f0 100644
--- a/app/javascript/mastodon/features/notifications/components/column_settings.js
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage } from 'react-intl';
@@ -7,7 +7,7 @@ import GrantPermissionButton from './grant_permission_button';
 import SettingToggle from './setting_toggle';
 import { isStaff } from 'mastodon/initial_state';
 
-export default class ColumnSettings extends React.PureComponent {
+export default class ColumnSettings extends PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/notifications/components/filter_bar.js b/app/javascript/mastodon/features/notifications/components/filter_bar.js
index 368eb0b7e6c0fa5afcfd730c23dc2757e5ba988e..9e9b154cd9d5aba6d449773c90100fe3cff82439 100644
--- a/app/javascript/mastodon/features/notifications/components/filter_bar.js
+++ b/app/javascript/mastodon/features/notifications/components/filter_bar.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import Icon from 'mastodon/components/icon';
@@ -13,7 +13,7 @@ const tooltips = defineMessages({
 });
 
 export default @injectIntl
-class FilterBar extends React.PureComponent {
+class FilterBar extends PureComponent {
 
   static propTypes = {
     selectFilter: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/notifications/components/follow_request.js b/app/javascript/mastodon/features/notifications/components/follow_request.js
index 9ef3fde7eb060bb2853839d0a87aa22509df266c..548871f451066365bba128037bb0a49abc6f3737 100644
--- a/app/javascript/mastodon/features/notifications/components/follow_request.js
+++ b/app/javascript/mastodon/features/notifications/components/follow_request.js
@@ -1,4 +1,3 @@
-import React, { Fragment } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from 'mastodon/components/avatar';
@@ -32,10 +31,10 @@ class FollowRequest extends ImmutablePureComponent {
 
     if (hidden) {
       return (
-        <Fragment>
+        <>
           {account.get('display_name')}
           {account.get('username')}
-        </Fragment>
+        </>
       );
     }
 
diff --git a/app/javascript/mastodon/features/notifications/components/grant_permission_button.js b/app/javascript/mastodon/features/notifications/components/grant_permission_button.js
index 798e4c7872b24d1eeb9a263bbd39dad4b92d5994..06420c9a14ccc505a9053be15849d318afc4f945 100644
--- a/app/javascript/mastodon/features/notifications/components/grant_permission_button.js
+++ b/app/javascript/mastodon/features/notifications/components/grant_permission_button.js
@@ -1,8 +1,8 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
 
-export default class GrantPermissionButton extends React.PureComponent {
+export default class GrantPermissionButton extends PureComponent {
 
   static propTypes = {
     onClick: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/notifications/components/notification.js b/app/javascript/mastodon/features/notifications/components/notification.js
index 0af71418c4c3d5be0d649ac5af47238bccb2bfa5..8261cffbb88f05552040e94ca097402afab6ef28 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.js
+++ b/app/javascript/mastodon/features/notifications/components/notification.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
 import { HotKeys } from 'react-hotkeys';
diff --git a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
index df9b7fb1b82561e44161d1446f21b80ba42873d4..30a1e17f39dcbdd0bf6f463828db1d822f5c6090 100644
--- a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
+++ b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import Icon from 'mastodon/components/icon';
 import Button from 'mastodon/components/button';
 import IconButton from 'mastodon/components/icon_button';
@@ -14,7 +14,7 @@ const messages = defineMessages({
 
 export default @connect()
 @injectIntl
-class NotificationsPermissionBanner extends React.PureComponent {
+class NotificationsPermissionBanner extends PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/notifications/components/report.js b/app/javascript/mastodon/features/notifications/components/report.js
index 3ce3eb9d32f5fab803f16c4e9db8152514185af2..75f87a4a456f68a1a0c4ff3fda599f541d7235df 100644
--- a/app/javascript/mastodon/features/notifications/components/report.js
+++ b/app/javascript/mastodon/features/notifications/components/report.js
@@ -1,4 +1,3 @@
-import React, { Fragment } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
@@ -32,9 +31,9 @@ class Report extends ImmutablePureComponent {
 
     if (hidden) {
       return (
-        <Fragment>
+        <>
           {report.get('id')}
-        </Fragment>
+        </>
       );
     }
 
diff --git a/app/javascript/mastodon/features/notifications/components/setting_toggle.js b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
index c4c8bffbe3240f9ce8f2124e71dcaaf93d3f33d6..b054b36ff11a6e36205e49f4ec4a2a7220e88bf5 100644
--- a/app/javascript/mastodon/features/notifications/components/setting_toggle.js
+++ b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Toggle from 'react-toggle';
 
-export default class SettingToggle extends React.PureComponent {
+export default class SettingToggle extends PureComponent {
 
   static propTypes = {
     prefix: PropTypes.string,
diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js
index a6a277d7ed9e2b0124408ca633c6abe917e789b9..0f32431850fd2ab5803e609f3e9248091cd5586b 100644
--- a/app/javascript/mastodon/features/notifications/index.js
+++ b/app/javascript/mastodon/features/notifications/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -67,7 +67,7 @@ const mapStateToProps = state => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class Notifications extends React.PureComponent {
+class Notifications extends PureComponent {
 
   static propTypes = {
     columnId: PropTypes.string,
@@ -89,7 +89,7 @@ class Notifications extends React.PureComponent {
     trackScroll: true,
   };
 
-  componentWillMount() {
+  UNSAFE_componentWillMount() {
     this.props.dispatch(mountNotifications());
   }
 
diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.js b/app/javascript/mastodon/features/picture_in_picture/components/footer.js
index 0cb42b25aac6527097f662217b28d20b5ee64654..74a0cf149323ab092eea8203b7d5186eeb6ad559 100644
--- a/app/javascript/mastodon/features/picture_in_picture/components/footer.js
+++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/javascript/mastodon/features/picture_in_picture/components/header.js b/app/javascript/mastodon/features/picture_in_picture/components/header.js
index e05d8c62e906c9b8c9bc5269b558dd88d34d92fb..58c35df6ec6f899b00309464c90382b22c36d3e3 100644
--- a/app/javascript/mastodon/features/picture_in_picture/components/header.js
+++ b/app/javascript/mastodon/features/picture_in_picture/components/header.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import ImmutablePropTypes from 'react-immutable-proptypes';
diff --git a/app/javascript/mastodon/features/picture_in_picture/index.js b/app/javascript/mastodon/features/picture_in_picture/index.js
index 1e59fbcd3376c46589d8ff2421dcd463e0c7313a..76a764c3603354937024d553a44a32cfdef7c7d1 100644
--- a/app/javascript/mastodon/features/picture_in_picture/index.js
+++ b/app/javascript/mastodon/features/picture_in_picture/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { Component } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import Video from 'mastodon/features/video';
@@ -12,7 +12,7 @@ const mapStateToProps = state => ({
 });
 
 export default @connect(mapStateToProps)
-class PictureInPicture extends React.Component {
+class PictureInPicture extends Component {
 
   static propTypes = {
     statusId: PropTypes.string,
diff --git a/app/javascript/mastodon/features/pinned_statuses/index.js b/app/javascript/mastodon/features/pinned_statuses/index.js
index 67b13f10aa7bfd3f1aa553980beffaa66fcbc23d..11e90419b70e69e770722ce8510673be59c00f49 100644
--- a/app/javascript/mastodon/features/pinned_statuses/index.js
+++ b/app/javascript/mastodon/features/pinned_statuses/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -30,7 +29,7 @@ class PinnedStatuses extends ImmutablePureComponent {
     multiColumn: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchPinnedStatuses());
   }
 
diff --git a/app/javascript/mastodon/features/public_timeline/components/column_settings.js b/app/javascript/mastodon/features/public_timeline/components/column_settings.js
index 756b6fe06da1749a4b54e7ae8b115cf646810587..78af8b5e37b997aa46fb6a6f8ec64e389e6d2f1b 100644
--- a/app/javascript/mastodon/features/public_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/public_timeline/components/column_settings.js
@@ -1,11 +1,11 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
 export default @injectIntl
-class ColumnSettings extends React.PureComponent {
+class ColumnSettings extends PureComponent {
 
   static propTypes = {
     settings: ImmutablePropTypes.map.isRequired,
diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.js
index b1d5518af556254a78be93ca975f4ad6c764ca57..8c7a817136433893ed41071a216c6ad7f5d59098 100644
--- a/app/javascript/mastodon/features/public_timeline/index.js
+++ b/app/javascript/mastodon/features/public_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
@@ -31,7 +31,7 @@ const mapStateToProps = (state, { columnId }) => {
 
 export default @connect(mapStateToProps)
 @injectIntl
-class PublicTimeline extends React.PureComponent {
+class PublicTimeline extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/reblogs/index.js b/app/javascript/mastodon/features/reblogs/index.js
index 7704a049f41198213834c5aed33c35d48b10e0e4..21c5a9854d9480ca1d153ded3e466f193f37cc79 100644
--- a/app/javascript/mastodon/features/reblogs/index.js
+++ b/app/javascript/mastodon/features/reblogs/index.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import PropTypes from 'prop-types';
@@ -32,13 +31,13 @@ class Reblogs extends ImmutablePureComponent {
     intl: PropTypes.object.isRequired,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     if (!this.props.accountIds) {
       this.props.dispatch(fetchReblogs(this.props.params.statusId));
     }
   }
 
-  componentWillReceiveProps(nextProps) {
+  UNSAFE_componentWillReceiveProps(nextProps) {
     if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
       this.props.dispatch(fetchReblogs(nextProps.params.statusId));
     }
diff --git a/app/javascript/mastodon/features/report/category.js b/app/javascript/mastodon/features/report/category.js
index 9215b3f51edfdc83bbbd5b5917bf368d85626bd8..a2592c9b5ec1fc711ec361bc8313b16e7e626686 100644
--- a/app/javascript/mastodon/features/report/category.js
+++ b/app/javascript/mastodon/features/report/category.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
@@ -25,7 +25,7 @@ const mapStateToProps = state => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class Category extends React.PureComponent {
+class Category extends PureComponent {
 
   static propTypes = {
     onNextStep: PropTypes.func.isRequired,
@@ -75,7 +75,7 @@ class Category extends React.PureComponent {
     ];
 
     return (
-      <React.Fragment>
+      <>
         <h3 className='report-dialog-modal__title'><FormattedMessage id='report.category.title' defaultMessage="Tell us what's going on with this {type}" values={{ type: intl.formatMessage(messages[startedFrom]) }} /></h3>
         <p className='report-dialog-modal__lead'><FormattedMessage id='report.category.subtitle' defaultMessage='Choose the best match' /></p>
 
@@ -98,7 +98,7 @@ class Category extends React.PureComponent {
         <div className='report-dialog-modal__actions'>
           <Button onClick={this.handleNextClick} disabled={category === null}><FormattedMessage id='report.next' defaultMessage='Next' /></Button>
         </div>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/features/report/comment.js b/app/javascript/mastodon/features/report/comment.js
index cde1564158024434eef9506f5c1d8914514b4231..250a2b3d2ae2c22dfd24638a0a81e6839187e3e9 100644
--- a/app/javascript/mastodon/features/report/comment.js
+++ b/app/javascript/mastodon/features/report/comment.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
 import Button from 'mastodon/components/button';
@@ -9,7 +9,7 @@ const messages = defineMessages({
 });
 
 export default @injectIntl
-class Comment extends React.PureComponent {
+class Comment extends PureComponent {
 
   static propTypes = {
     onSubmit: PropTypes.func.isRequired,
@@ -48,7 +48,7 @@ class Comment extends React.PureComponent {
     const { comment, isRemote, forward, domain, isSubmitting, intl } = this.props;
 
     return (
-      <React.Fragment>
+      <>
         <h3 className='report-dialog-modal__title'><FormattedMessage id='report.comment.title' defaultMessage='Is there anything else you think we should know?' /></h3>
 
         <textarea
@@ -61,14 +61,14 @@ class Comment extends React.PureComponent {
         />
 
         {isRemote && (
-          <React.Fragment>
+          <>
             <p className='report-dialog-modal__lead'><FormattedMessage id='report.forward_hint' defaultMessage='The account is from another server. Send an anonymized copy of the report there as well?' /></p>
 
             <label className='report-dialog-modal__toggle'>
               <Toggle checked={forward} disabled={isSubmitting} onChange={this.handleForwardChange} />
               <FormattedMessage id='report.forward' defaultMessage='Forward to {target}' values={{ target: domain }} />
             </label>
-          </React.Fragment>
+          </>
         )}
 
         <div className='flex-spacer' />
@@ -76,7 +76,7 @@ class Comment extends React.PureComponent {
         <div className='report-dialog-modal__actions'>
           <Button onClick={this.handleClick} disabled={isSubmitting}><FormattedMessage id='report.submit' defaultMessage='Submit report' /></Button>
         </div>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/features/report/components/option.js b/app/javascript/mastodon/features/report/components/option.js
index 744d8526803d3829a6ae5ca797d270012846acf5..df6ef0e0f312d75666d52f02b9f85c24a1cffa62 100644
--- a/app/javascript/mastodon/features/report/components/option.js
+++ b/app/javascript/mastodon/features/report/components/option.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import Check from 'mastodon/components/check';
 
-export default class Option extends React.PureComponent {
+export default class Option extends PureComponent {
 
   static propTypes = {
     name: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/report/components/status_check_box.js b/app/javascript/mastodon/features/report/components/status_check_box.js
index 373c60e21e294455a3a2a8b481028839088f7a5f..a8abcfaa2531bcbc6971877c4f9524b13fe8682b 100644
--- a/app/javascript/mastodon/features/report/components/status_check_box.js
+++ b/app/javascript/mastodon/features/report/components/status_check_box.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import StatusContent from 'mastodon/components/status_content';
@@ -8,7 +8,7 @@ import RelativeTimestamp from 'mastodon/components/relative_timestamp';
 import Option from './option';
 import MediaAttachments from 'mastodon/components/media_attachments';
 
-export default class StatusCheckBox extends React.PureComponent {
+export default class StatusCheckBox extends PureComponent {
 
   static propTypes = {
     id: PropTypes.string.isRequired,
diff --git a/app/javascript/mastodon/features/report/rules.js b/app/javascript/mastodon/features/report/rules.js
index f2db0d9e4cc42ce354b5f69bf927c59f553288d8..e804d195cb3a57c130f945f072214101fb957e33 100644
--- a/app/javascript/mastodon/features/report/rules.js
+++ b/app/javascript/mastodon/features/report/rules.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
@@ -11,7 +11,7 @@ const mapStateToProps = state => ({
 });
 
 export default @connect(mapStateToProps)
-class Rules extends React.PureComponent {
+class Rules extends PureComponent {
 
   static propTypes = {
     onNextStep: PropTypes.func.isRequired,
@@ -34,7 +34,7 @@ class Rules extends React.PureComponent {
     const { rules, selectedRuleIds } = this.props;
 
     return (
-      <React.Fragment>
+      <>
         <h3 className='report-dialog-modal__title'><FormattedMessage id='report.rules.title' defaultMessage='Which rules are being violated?' /></h3>
         <p className='report-dialog-modal__lead'><FormattedMessage id='report.rules.subtitle' defaultMessage='Select all that apply' /></p>
 
@@ -57,7 +57,7 @@ class Rules extends React.PureComponent {
         <div className='report-dialog-modal__actions'>
           <Button onClick={this.handleNextClick} disabled={selectedRuleIds.size < 1}><FormattedMessage id='report.next' defaultMessage='Next' /></Button>
         </div>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/features/report/statuses.js b/app/javascript/mastodon/features/report/statuses.js
index d5d86034fb5a9296845caa94f4c4b575b269194c..3ced1111038b67ee1fa183bc5077177e617f1487 100644
--- a/app/javascript/mastodon/features/report/statuses.js
+++ b/app/javascript/mastodon/features/report/statuses.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
@@ -14,7 +14,7 @@ const mapStateToProps = (state, { accountId }) => ({
 });
 
 export default @connect(mapStateToProps)
-class Statuses extends React.PureComponent {
+class Statuses extends PureComponent {
 
   static propTypes = {
     onNextStep: PropTypes.func.isRequired,
@@ -34,7 +34,7 @@ class Statuses extends React.PureComponent {
     const { availableStatusIds, selectedStatusIds, onToggle, isLoading } = this.props;
 
     return (
-      <React.Fragment>
+      <>
         <h3 className='report-dialog-modal__title'><FormattedMessage id='report.statuses.title' defaultMessage='Are there any posts that back up this report?' /></h3>
         <p className='report-dialog-modal__lead'><FormattedMessage id='report.statuses.subtitle' defaultMessage='Select all that apply' /></p>
 
@@ -54,7 +54,7 @@ class Statuses extends React.PureComponent {
         <div className='report-dialog-modal__actions'>
           <Button onClick={this.handleNextClick}><FormattedMessage id='report.next' defaultMessage='Next' /></Button>
         </div>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/features/report/thanks.js b/app/javascript/mastodon/features/report/thanks.js
index d169b1e329472e7278471eba4b3cc685f535bd5b..a58e948763d5f5c2c3f05e88798db5ec7476219f 100644
--- a/app/javascript/mastodon/features/report/thanks.js
+++ b/app/javascript/mastodon/features/report/thanks.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { FormattedMessage } from 'react-intl';
@@ -13,7 +13,7 @@ import {
 const mapStateToProps = () => ({});
 
 export default @connect(mapStateToProps)
-class Thanks extends React.PureComponent {
+class Thanks extends PureComponent {
 
   static propTypes = {
     submitted: PropTypes.bool,
@@ -49,17 +49,17 @@ class Thanks extends React.PureComponent {
     const { account, submitted } = this.props;
 
     return (
-      <React.Fragment>
+      <>
         <h3 className='report-dialog-modal__title'>{submitted ? <FormattedMessage id='report.thanks.title_actionable' defaultMessage="Thanks for reporting, we'll look into this." /> : <FormattedMessage id='report.thanks.title' defaultMessage="Don't want to see this?" />}</h3>
         <p className='report-dialog-modal__lead'>{submitted ? <FormattedMessage id='report.thanks.take_action_actionable' defaultMessage='While we review this, you can take action against @{name}:' values={{ name: account.get('username') }} /> : <FormattedMessage id='report.thanks.take_action' defaultMessage='Here are your options for controlling what you see on Mastodon:' />}</p>
 
         {account.getIn(['relationship', 'following']) && (
-          <React.Fragment>
+          <>
             <h4 className='report-dialog-modal__subtitle'><FormattedMessage id='report.unfollow' defaultMessage='Unfollow @{name}' values={{ name: account.get('username') }} /></h4>
             <p className='report-dialog-modal__lead'><FormattedMessage id='report.unfollow_explanation' defaultMessage='You are following this account. To not see their posts in your home feed anymore, unfollow them.' /></p>
             <Button secondary onClick={this.handleUnfollowClick}><FormattedMessage id='account.unfollow' defaultMessage='Unfollow' /></Button>
             <hr />
-          </React.Fragment>
+          </>
         )}
 
         <h4 className='report-dialog-modal__subtitle'><FormattedMessage id='account.mute' defaultMessage='Mute @{name}' values={{ name: account.get('username') }} /></h4>
@@ -77,7 +77,7 @@ class Thanks extends React.PureComponent {
         <div className='report-dialog-modal__actions'>
           <Button onClick={this.handleCloseClick}><FormattedMessage id='report.close' defaultMessage='Done' /></Button>
         </div>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/features/standalone/compose/index.js b/app/javascript/mastodon/features/standalone/compose/index.js
index 0d764575fd73b4df1e56039030a55301ed20a20f..610000036e301fd60fef8bef9ab459add274d163 100644
--- a/app/javascript/mastodon/features/standalone/compose/index.js
+++ b/app/javascript/mastodon/features/standalone/compose/index.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ComposeFormContainer from '../../compose/containers/compose_form_container';
 import NotificationsContainer from '../../ui/containers/notifications_container';
 import LoadingBarContainer from '../../ui/containers/loading_bar_container';
 import ModalContainer from '../../ui/containers/modal_container';
 
-export default class Compose extends React.PureComponent {
+export default class Compose extends PureComponent {
 
   render () {
     return (
diff --git a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
index d3d8a6507e6d19701386a3e20b4dacae89f603dc..f23c5c1f53d6eacf7d4dcc41e95ea87b048a7f2a 100644
--- a/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
+++ b/app/javascript/mastodon/features/standalone/hashtag_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -16,7 +16,7 @@ const mapStateToProps = (state, { hashtag }) => ({
 });
 
 export default @connect(mapStateToProps)
-class HashtagTimeline extends React.PureComponent {
+class HashtagTimeline extends PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/standalone/public_timeline/index.js b/app/javascript/mastodon/features/standalone/public_timeline/index.js
index 19b0b14be6a35d2decfb9f4f89825672b397f222..6a3530bde18845d4ca8f8a60c4fd8900d2ae788f 100644
--- a/app/javascript/mastodon/features/standalone/public_timeline/index.js
+++ b/app/javascript/mastodon/features/standalone/public_timeline/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -20,7 +20,7 @@ const mapStateToProps = (state, { local }) => {
 };
 
 export default @connect(mapStateToProps)
-class PublicTimeline extends React.PureComponent {
+class PublicTimeline extends PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index edaff959e6cd5d5f735136abd933f0f95f741922..2ece806f1af4826656fa063e1071f8dc8564d26d 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { connect } from 'react-redux';
 import IconButton from '../../../components/icon_button';
@@ -46,7 +46,7 @@ const mapStateToProps = (state, { status }) => ({
 
 export default @connect(mapStateToProps)
 @injectIntl
-class ActionBar extends React.PureComponent {
+class ActionBar extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
diff --git a/app/javascript/mastodon/features/status/components/card.js b/app/javascript/mastodon/features/status/components/card.js
index 3d81bcb29b626d66303b3098295926041ae953b6..cad1526f300c61e0a66f762a599857d66b06e345 100644
--- a/app/javascript/mastodon/features/status/components/card.js
+++ b/app/javascript/mastodon/features/status/components/card.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import Immutable from 'immutable';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -58,7 +58,7 @@ const addAutoPlay = html => {
   return html;
 };
 
-export default class Card extends React.PureComponent {
+export default class Card extends PureComponent {
 
   static propTypes = {
     card: ImmutablePropTypes.map,
@@ -82,7 +82,7 @@ export default class Card extends React.PureComponent {
     revealed: !this.props.sensitive,
   };
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (!Immutable.is(this.props.card, nextProps.card)) {
       this.setState({ embedded: false, previewLoaded: false });
     }
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js
index 13bce1b831f1e1ea301d3be1499d9edeaeec89d5..6d7aaaea83ec54c6ce5889daf18bbf832ce1653c 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.js
+++ b/app/javascript/mastodon/features/status/components/detailed_status.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import Avatar from '../../../components/avatar';
@@ -178,7 +177,7 @@ class DetailedStatus extends ImmutablePureComponent {
     }
 
     if (status.get('application')) {
-      applicationLink = <React.Fragment> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></React.Fragment>;
+      applicationLink = <> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></>;
     }
 
     const visibilityIconInfo = {
@@ -189,33 +188,33 @@ class DetailedStatus extends ImmutablePureComponent {
     };
 
     const visibilityIcon = visibilityIconInfo[status.get('visibility')];
-    const visibilityLink = <React.Fragment> · <Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></React.Fragment>;
+    const visibilityLink = <> · <Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></>;
 
     if (['private', 'direct'].includes(status.get('visibility'))) {
       reblogLink = '';
     } else if (this.context.router) {
       reblogLink = (
-        <React.Fragment>
-          <React.Fragment> · </React.Fragment>
+        <>
+          <> · </>
           <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'>
             <Icon id={reblogIcon} />
             <span className='detailed-status__reblogs'>
               <AnimatedNumber value={status.get('reblogs_count')} />
             </span>
           </Link>
-        </React.Fragment>
+        </>
       );
     } else {
       reblogLink = (
-        <React.Fragment>
-          <React.Fragment> · </React.Fragment>
+        <>
+          <> · </>
           <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}>
             <Icon id={reblogIcon} />
             <span className='detailed-status__reblogs'>
               <AnimatedNumber value={status.get('reblogs_count')} />
             </span>
           </a>
-        </React.Fragment>
+        </>
       );
     }
 
@@ -241,10 +240,10 @@ class DetailedStatus extends ImmutablePureComponent {
 
     if (status.get('edited_at')) {
       edited = (
-        <React.Fragment>
-          <React.Fragment> · </React.Fragment>
+        <>
+          <> · </>
           <EditedTimestamp statusId={status.get('id')} timestamp={status.get('edited_at')} />
-        </React.Fragment>
+        </>
       );
     }
 
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js
index 4d7f24834e42d9fdd3d737ef7443b71b0c4ef301..e54945368ce74fd51906e51bb5caf8c55a1bbfee 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.js
@@ -1,5 +1,4 @@
 import Immutable from 'immutable';
-import React from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
@@ -184,7 +183,7 @@ class Status extends ImmutablePureComponent {
     loadedStatusId: undefined,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     this.props.dispatch(fetchStatus(this.props.params.statusId));
   }
 
@@ -192,7 +191,7 @@ class Status extends ImmutablePureComponent {
     attachFullscreenListener(this.onFullScreenChange);
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (nextProps.params.statusId !== this.props.params.statusId && nextProps.params.statusId) {
       this._scrolledIntoView = false;
       this.props.dispatch(fetchStatus(nextProps.params.statusId));
diff --git a/app/javascript/mastodon/features/ui/components/__tests__/column-test.js b/app/javascript/mastodon/features/ui/components/__tests__/column-test.js
index a56859be07bd9f6a212fff6d90958d2246971412..8f935299fa2b4dc14dda6b2f86d290a41b413ece 100644
--- a/app/javascript/mastodon/features/ui/components/__tests__/column-test.js
+++ b/app/javascript/mastodon/features/ui/components/__tests__/column-test.js
@@ -1,5 +1,4 @@
 import { render, fireEvent, screen } from '@testing-library/react';
-import React from 'react';
 import Column from '../column';
 
 describe('<Column />', () => {
diff --git a/app/javascript/mastodon/features/ui/components/actions_modal.js b/app/javascript/mastodon/features/ui/components/actions_modal.js
index 875b2b75d7e08754c0d53de7a288041fcc0eabc5..31318406fab449cbb30c897c0c561b3a22299c72 100644
--- a/app/javascript/mastodon/features/ui/components/actions_modal.js
+++ b/app/javascript/mastodon/features/ui/components/actions_modal.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/ui/components/audio_modal.js b/app/javascript/mastodon/features/ui/components/audio_modal.js
index c46fefce89d17bb9448dc07f9e27013eb3dc09ec..fbf4c6151e93ebb8557f3999e0954b607b44af45 100644
--- a/app/javascript/mastodon/features/ui/components/audio_modal.js
+++ b/app/javascript/mastodon/features/ui/components/audio_modal.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Audio from 'mastodon/features/audio';
diff --git a/app/javascript/mastodon/features/ui/components/block_modal.js b/app/javascript/mastodon/features/ui/components/block_modal.js
index a07baeaa64a450768dc2ede4e01744550a3d3a2e..3445cdbb3ca02d70d12da6419dfe803e8edf78ca 100644
--- a/app/javascript/mastodon/features/ui/components/block_modal.js
+++ b/app/javascript/mastodon/features/ui/components/block_modal.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { injectIntl, FormattedMessage } from 'react-intl';
@@ -38,7 +38,7 @@ const mapDispatchToProps = dispatch => {
 
 export default @connect(makeMapStateToProps, mapDispatchToProps)
 @injectIntl
-class BlockModal extends React.PureComponent {
+class BlockModal extends PureComponent {
 
   static propTypes = {
     account: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/boost_modal.js b/app/javascript/mastodon/features/ui/components/boost_modal.js
index ab87ee4276918786b8e2ce276988741dfd003646..9c0c18ad42e82e9db85494819f6b9611d99c0e13 100644
--- a/app/javascript/mastodon/features/ui/components/boost_modal.js
+++ b/app/javascript/mastodon/features/ui/components/boost_modal.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/ui/components/bundle.js b/app/javascript/mastodon/features/ui/components/bundle.js
index a60ace35ba670ebc37efa82763e9f6c29b396980..7805f18dd5424423d2920765676a83f984cc9df8 100644
--- a/app/javascript/mastodon/features/ui/components/bundle.js
+++ b/app/javascript/mastodon/features/ui/components/bundle.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 
 const emptyComponent = () => null;
 const noop = () => { };
 
-class Bundle extends React.PureComponent {
+class Bundle extends PureComponent {
 
   static propTypes = {
     fetchComponent: PropTypes.func.isRequired,
@@ -33,11 +33,11 @@ class Bundle extends React.PureComponent {
     forceRender: false,
   }
 
-  componentWillMount() {
+  UNSAFE_componentWillMount() {
     this.load(this.props);
   }
 
-  componentWillReceiveProps(nextProps) {
+  UNSAFE_componentWillReceiveProps(nextProps) {
     if (nextProps.fetchComponent !== this.props.fetchComponent) {
       this.load(nextProps);
     }
diff --git a/app/javascript/mastodon/features/ui/components/bundle_column_error.js b/app/javascript/mastodon/features/ui/components/bundle_column_error.js
index f39ebd900b103e77b6f7c7b3be933f481cb6de71..38ad48a32b43397c073e9df80bba0890c0788387 100644
--- a/app/javascript/mastodon/features/ui/components/bundle_column_error.js
+++ b/app/javascript/mastodon/features/ui/components/bundle_column_error.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
 
@@ -13,7 +13,7 @@ const messages = defineMessages({
   retry: { id: 'bundle_column_error.retry', defaultMessage: 'Try again' },
 });
 
-class BundleColumnError extends React.PureComponent {
+class BundleColumnError extends PureComponent {
 
   static propTypes = {
     onRetry: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/bundle_modal_error.js b/app/javascript/mastodon/features/ui/components/bundle_modal_error.js
index f9365b95bcca3598499c3a7e9683388f775b26de..d78008832cd8a4999c5a2927ceb1e18d9f226ab2 100644
--- a/app/javascript/mastodon/features/ui/components/bundle_modal_error.js
+++ b/app/javascript/mastodon/features/ui/components/bundle_modal_error.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
 
@@ -10,7 +10,7 @@ const messages = defineMessages({
   close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
 });
 
-class BundleModalError extends React.PureComponent {
+class BundleModalError extends PureComponent {
 
   static propTypes = {
     onRetry: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/column.js b/app/javascript/mastodon/features/ui/components/column.js
index 15538ea387a66d109b94e5c641d261ad31179a7f..91ecf74523d91aeca0b6b90e89b477b58d534dc8 100644
--- a/app/javascript/mastodon/features/ui/components/column.js
+++ b/app/javascript/mastodon/features/ui/components/column.js
@@ -1,11 +1,11 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ColumnHeader from './column_header';
 import PropTypes from 'prop-types';
 import { debounce } from 'lodash';
 import { scrollTop } from '../../../scroll';
 import { isMobile } from '../../../is_mobile';
 
-export default class Column extends React.PureComponent {
+export default class Column extends PureComponent {
 
   static propTypes = {
     heading: PropTypes.string,
diff --git a/app/javascript/mastodon/features/ui/components/column_header.js b/app/javascript/mastodon/features/ui/components/column_header.js
index b1a36e173d70a520345aa0356962a1cbdc585e16..6360ceb9804c7b5409c004ee3ccfcd70ada76416 100644
--- a/app/javascript/mastodon/features/ui/components/column_header.js
+++ b/app/javascript/mastodon/features/ui/components/column_header.js
@@ -1,9 +1,9 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import Icon from 'mastodon/components/icon';
 
-export default class ColumnHeader extends React.PureComponent {
+export default class ColumnHeader extends PureComponent {
 
   static propTypes = {
     icon: PropTypes.string,
diff --git a/app/javascript/mastodon/features/ui/components/column_link.js b/app/javascript/mastodon/features/ui/components/column_link.js
index 0a25f1ea203d7b7a0ea12d8794a4608bc8d3d797..5c6de4d858d809da50b87446dc75d3405eef9efa 100644
--- a/app/javascript/mastodon/features/ui/components/column_link.js
+++ b/app/javascript/mastodon/features/ui/components/column_link.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import { Link } from 'react-router-dom';
 import Icon from 'mastodon/components/icon';
diff --git a/app/javascript/mastodon/features/ui/components/column_loading.js b/app/javascript/mastodon/features/ui/components/column_loading.js
index 0cdfd05d80e2a4248b555093c1593f6fe551d648..87da4f42092f8a24febc9394d73b4d17dc42d354 100644
--- a/app/javascript/mastodon/features/ui/components/column_loading.js
+++ b/app/javascript/mastodon/features/ui/components/column_loading.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 
 import Column from '../../../components/column';
diff --git a/app/javascript/mastodon/features/ui/components/column_subheading.js b/app/javascript/mastodon/features/ui/components/column_subheading.js
index 8160c4aa394fc4527fd3960e1f6ee4f221cff9b4..e970a0bfdd0e1e2ceb37ecbc2505c73e60ebfcc3 100644
--- a/app/javascript/mastodon/features/ui/components/column_subheading.js
+++ b/app/javascript/mastodon/features/ui/components/column_subheading.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 
 const ColumnSubheading = ({ text }) => {
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js
index 68017a5f1ef8a64f6215317c129228ee052f67a4..0ae2e561542265bebaafddf351df30468f3a5324 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area.js
+++ b/app/javascript/mastodon/features/ui/components/columns_area.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { Children, cloneElement } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl } from 'react-intl';
 import ImmutablePropTypes from 'react-immutable-proptypes';
@@ -78,7 +78,7 @@ class ColumnsArea extends ImmutablePureComponent {
     renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches),
   }
 
-  componentWillReceiveProps() {
+  UNSAFE_componentWillReceiveProps() {
     if (typeof this.pendingIndex !== 'number' && this.lastIndex !== getIndex(this.context.router.history.location.pathname)) {
       this.setState({ shouldAnimate: false });
     }
@@ -104,7 +104,7 @@ class ColumnsArea extends ImmutablePureComponent {
     this.setState({ shouldAnimate: true });
   }
 
-  componentWillUpdate(nextProps) {
+  UNSAFE_componentWillUpdate(nextProps) {
     if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
       this.node.removeEventListener('wheel', this.handleWheel);
     }
@@ -191,7 +191,7 @@ class ColumnsArea extends ImmutablePureComponent {
     const icon = link.props['data-preview-icon'];
 
     const view = (index === columnIndex) ?
-      React.cloneElement(this.props.children) :
+      cloneElement(this.props.children) :
       <ColumnLoading title={title} icon={icon} />;
 
     return (
@@ -263,7 +263,7 @@ class ColumnsArea extends ImmutablePureComponent {
           );
         })}
 
-        {React.Children.map(children, child => React.cloneElement(child, { multiColumn: true }))}
+        {Children.map(children, child => cloneElement(child, { multiColumn: true }))}
       </div>
     );
   }
diff --git a/app/javascript/mastodon/features/ui/components/compare_history_modal.js b/app/javascript/mastodon/features/ui/components/compare_history_modal.js
index ecccc8f7df586b31d043c5896d814bbeb06fde5a..116347da2407367b77e963a9d6b2591e4a83dc7a 100644
--- a/app/javascript/mastodon/features/ui/components/compare_history_modal.js
+++ b/app/javascript/mastodon/features/ui/components/compare_history_modal.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
@@ -24,7 +24,7 @@ const mapDispatchToProps = dispatch => ({
 });
 
 export default @connect(mapStateToProps, mapDispatchToProps)
-class CompareHistoryModal extends React.PureComponent {
+class CompareHistoryModal extends PureComponent {
 
   static propTypes = {
     onClose: PropTypes.func.isRequired,
@@ -64,10 +64,10 @@ class CompareHistoryModal extends React.PureComponent {
         <div className='compare-history-modal__container'>
           <div className='status__content'>
             {currentVersion.get('spoiler_text').length > 0 && (
-              <React.Fragment>
+              <>
                 <div className='translate' dangerouslySetInnerHTML={spoilerContent} />
                 <hr />
-              </React.Fragment>
+              </>
             )}
 
             <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
diff --git a/app/javascript/mastodon/features/ui/components/compose_panel.js b/app/javascript/mastodon/features/ui/components/compose_panel.js
index 3d0c48c7a9687c759106c83a8d918940d78e12da..ba0de17f1dbf2357d9beeba98614e99f2e550a8e 100644
--- a/app/javascript/mastodon/features/ui/components/compose_panel.js
+++ b/app/javascript/mastodon/features/ui/components/compose_panel.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import SearchContainer from 'mastodon/features/compose/containers/search_container';
@@ -8,7 +8,7 @@ import LinkFooter from './link_footer';
 import { changeComposing } from 'mastodon/actions/compose';
 
 export default @connect()
-class ComposePanel extends React.PureComponent {
+class ComposePanel extends PureComponent {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modal.js b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
index 65d97ca1610c95178e8a303c9b1f9289a87f828f..648ef85d683961a2b9a9d91cbd8e9c9960fb3b69 100644
--- a/app/javascript/mastodon/features/ui/components/confirmation_modal.js
+++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import Button from '../../../components/button';
 
 export default @injectIntl
-class ConfirmationModal extends React.PureComponent {
+class ConfirmationModal extends PureComponent {
 
   static propTypes = {
     message: PropTypes.node.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/drawer_loading.js b/app/javascript/mastodon/features/ui/components/drawer_loading.js
index 08b0d234767fad227a0e904364262413994b8c23..11072ad98cc076d63c7e51f1c2d30612b355f0be 100644
--- a/app/javascript/mastodon/features/ui/components/drawer_loading.js
+++ b/app/javascript/mastodon/features/ui/components/drawer_loading.js
@@ -1,5 +1,3 @@
-import React from 'react';
-
 const DrawerLoading = () => (
   <div className='drawer'>
     <div className='drawer__pager'>
diff --git a/app/javascript/mastodon/features/ui/components/embed_modal.js b/app/javascript/mastodon/features/ui/components/embed_modal.js
index 4679c9650fdc439bc3a2f49eea90e7ed8ba5f7b1..eb88dbe1f2e716f3a9791c9cccc6c4784107f420 100644
--- a/app/javascript/mastodon/features/ui/components/embed_modal.js
+++ b/app/javascript/mastodon/features/ui/components/embed_modal.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
index a2e6b3d16b291e7e365576fd9ae59a58e81cddda..4655d000a003802e84da309cf2fef2cf24323602 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import ImmutablePureComponent from 'react-immutable-pure-component';
@@ -69,7 +69,7 @@ const removeExtraLineBreaks = str => str.replace(/\n\n/g, '******')
   .replace(/\n/g, ' ')
   .replace(/\*\*\*\*\*\*/g, '\n\n');
 
-class ImageLoader extends React.PureComponent {
+class ImageLoader extends PureComponent {
 
   static propTypes = {
     src: PropTypes.string.isRequired,
@@ -317,7 +317,7 @@ class FocalPointModal extends ImmutablePureComponent {
             {focals && <p><FormattedMessage id='upload_modal.hint' defaultMessage='Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.' /></p>}
 
             {thumbnailable && (
-              <React.Fragment>
+              <>
                 <label className='setting-text-label' htmlFor='upload-modal__thumbnail'><FormattedMessage id='upload_form.thumbnail' defaultMessage='Change thumbnail' /></label>
 
                 <Button disabled={isUploadingThumbnail} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
@@ -337,7 +337,7 @@ class FocalPointModal extends ImmutablePureComponent {
                 </label>
 
                 <hr className='setting-divider' />
-              </React.Fragment>
+              </>
             )}
 
             <label className='setting-text-label' htmlFor='upload-modal__description'>
diff --git a/app/javascript/mastodon/features/ui/components/follow_requests_nav_link.js b/app/javascript/mastodon/features/ui/components/follow_requests_nav_link.js
index 950ed7b273ce8aa2cef373a90b6e5cbce72ef00b..9d48a4cf12f6b65143e3c873f2aa9dd0acaee6eb 100644
--- a/app/javascript/mastodon/features/ui/components/follow_requests_nav_link.js
+++ b/app/javascript/mastodon/features/ui/components/follow_requests_nav_link.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { Component } from 'react';
 import PropTypes from 'prop-types';
 import { fetchFollowRequests } from 'mastodon/actions/accounts';
 import { connect } from 'react-redux';
@@ -13,7 +13,7 @@ const mapStateToProps = state => ({
 
 export default @withRouter
 @connect(mapStateToProps)
-class FollowRequestsNavLink extends React.Component {
+class FollowRequestsNavLink extends Component {
 
   static propTypes = {
     dispatch: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.js
index c6f16a79237e44ee6cd205e5e2196045c3bbe222..2d3dc09a942367a6682cb66f3c05857eed6fb861 100644
--- a/app/javascript/mastodon/features/ui/components/image_loader.js
+++ b/app/javascript/mastodon/features/ui/components/image_loader.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import { LoadingBar } from 'react-redux-loading-bar';
 import ZoomableImage from './zoomable_image';
 
-export default class ImageLoader extends React.PureComponent {
+export default class ImageLoader extends PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
@@ -43,7 +43,7 @@ export default class ImageLoader extends React.PureComponent {
     this.loadImage(this.props);
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (this.props.src !== nextProps.src) {
       this.loadImage(nextProps);
     }
diff --git a/app/javascript/mastodon/features/ui/components/link_footer.js b/app/javascript/mastodon/features/ui/components/link_footer.js
index edf1104c4ed626645b3e5a10d628300e0b37ef4b..6196825d2026ee48156c79fff1eea87005480074 100644
--- a/app/javascript/mastodon/features/ui/components/link_footer.js
+++ b/app/javascript/mastodon/features/ui/components/link_footer.js
@@ -1,5 +1,5 @@
 import { connect } from 'react-redux';
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
 import { Link } from 'react-router-dom';
@@ -25,7 +25,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
 
 export default @injectIntl
 @connect(null, mapDispatchToProps)
-class LinkFooter extends React.PureComponent {
+class LinkFooter extends PureComponent {
 
   static propTypes = {
     withHotkeys: PropTypes.bool,
diff --git a/app/javascript/mastodon/features/ui/components/list_panel.js b/app/javascript/mastodon/features/ui/components/list_panel.js
index 411f62508ed02f430ca30b43854c63666502442b..9196276034227940cc18d0cb9152418527abf176 100644
--- a/app/javascript/mastodon/features/ui/components/list_panel.js
+++ b/app/javascript/mastodon/features/ui/components/list_panel.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import ImmutablePureComponent from 'react-immutable-pure-component';
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js
index ae937d1cdf90cec13593698d4946256cfb2bdd84..4d1ac43d7f73dd08e7e0fa340e76ff80b83f409e 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.js
+++ b/app/javascript/mastodon/features/ui/components/media_modal.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ReactSwipeableViews from 'react-swipeable-views';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
diff --git a/app/javascript/mastodon/features/ui/components/modal_loading.js b/app/javascript/mastodon/features/ui/components/modal_loading.js
index f403ca4c9e90436bb41d3c46dde76641b65a3c17..7a6bbf5fa21cd754ae760948dade34e35cbf5d96 100644
--- a/app/javascript/mastodon/features/ui/components/modal_loading.js
+++ b/app/javascript/mastodon/features/ui/components/modal_loading.js
@@ -1,5 +1,3 @@
-import React from 'react';
-
 import LoadingIndicator from '../../../components/loading_indicator';
 
 // Keep the markup in sync with <BundleModalError />
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js
index 3fc235849df783ae58c0926596649d8c82a5cd99..020d5b2367bfc83e6f762147907b6cf8ef781292 100644
--- a/app/javascript/mastodon/features/ui/components/modal_root.js
+++ b/app/javascript/mastodon/features/ui/components/modal_root.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
 import Base from 'mastodon/components/modal_root';
@@ -39,7 +39,7 @@ const MODAL_COMPONENTS = {
   'COMPARE_HISTORY': CompareHistoryModal,
 };
 
-export default class ModalRoot extends React.PureComponent {
+export default class ModalRoot extends PureComponent {
 
   static propTypes = {
     type: PropTypes.string,
diff --git a/app/javascript/mastodon/features/ui/components/mute_modal.js b/app/javascript/mastodon/features/ui/components/mute_modal.js
index d8d8e68c384dbc22233721cd1361581a901f4166..f7f1b875ab7c530a3a31fc0a1eb089c630f8c289 100644
--- a/app/javascript/mastodon/features/ui/components/mute_modal.js
+++ b/app/javascript/mastodon/features/ui/components/mute_modal.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
@@ -45,7 +45,7 @@ const mapDispatchToProps = dispatch => {
 
 export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
-class MuteModal extends React.PureComponent {
+class MuteModal extends PureComponent {
 
   static propTypes = {
     account: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.js
index fe4ed5d7721e63de305c12719af220e1aa30b59a..c82eb142da2e0a4eed6fcd0b8932657d935cf475 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.js
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { NavLink, withRouter } from 'react-router-dom';
 import { FormattedMessage } from 'react-intl';
 import Icon from 'mastodon/components/icon';
diff --git a/app/javascript/mastodon/features/ui/components/report_modal.js b/app/javascript/mastodon/features/ui/components/report_modal.js
index 744dd248b5891d7220bdc22692237f1fb1fab1dd..fdcc5706b8f5ccad2dea8bc9c337ec3dc3689792 100644
--- a/app/javascript/mastodon/features/ui/components/report_modal.js
+++ b/app/javascript/mastodon/features/ui/components/report_modal.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import { connect } from 'react-redux';
 import { submitReport } from 'mastodon/actions/reports';
 import { expandAccountTimeline } from 'mastodon/actions/timelines';
diff --git a/app/javascript/mastodon/features/ui/components/tabs_bar.js b/app/javascript/mastodon/features/ui/components/tabs_bar.js
index 55668cab6a238247a64a9d5f678de0f5da4d8ff8..281dbd3ca221c66664b0f350775f1f271ff034d3 100644
--- a/app/javascript/mastodon/features/ui/components/tabs_bar.js
+++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent, cloneElement } from 'react';
 import PropTypes from 'prop-types';
 import { NavLink, withRouter } from 'react-router-dom';
 import { FormattedMessage, injectIntl } from 'react-intl';
@@ -26,7 +26,7 @@ export function getLink (index) {
 
 export default @injectIntl
 @withRouter
-class TabsBar extends React.PureComponent {
+class TabsBar extends PureComponent {
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
@@ -75,7 +75,7 @@ class TabsBar extends React.PureComponent {
     return (
       <div className='tabs-bar__wrapper'>
         <nav className='tabs-bar' ref={this.setRef}>
-          {links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
+          {links.map(link => cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
         </nav>
 
         <div id='tabs-bar__portal' />
diff --git a/app/javascript/mastodon/features/ui/components/upload_area.js b/app/javascript/mastodon/features/ui/components/upload_area.js
index 6c423b2c1d8d5b6a7a29abb05960b708f9513561..70bad0dc4c6103c6d90d90f6171de640905c7fac 100644
--- a/app/javascript/mastodon/features/ui/components/upload_area.js
+++ b/app/javascript/mastodon/features/ui/components/upload_area.js
@@ -1,10 +1,10 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import Motion from '../../ui/util/optional_motion';
 import spring from 'react-motion/lib/spring';
 import { FormattedMessage } from 'react-intl';
 
-export default class UploadArea extends React.PureComponent {
+export default class UploadArea extends PureComponent {
 
   static propTypes = {
     active: PropTypes.bool,
diff --git a/app/javascript/mastodon/features/ui/components/video_modal.js b/app/javascript/mastodon/features/ui/components/video_modal.js
index f3ee89dfd91f9e6242c2fabcfb95c7c36b7d450b..a0c4cea3b835565db5d9e689293ac8e8584d014c 100644
--- a/app/javascript/mastodon/features/ui/components/video_modal.js
+++ b/app/javascript/mastodon/features/ui/components/video_modal.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'mastodon/features/video';
diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.js b/app/javascript/mastodon/features/ui/components/zoomable_image.js
index 1cf263cb9003d5b081e141145b7105de1b62108c..1fc26964de8998836c8f519918ad73ff9d03eda9 100644
--- a/app/javascript/mastodon/features/ui/components/zoomable_image.js
+++ b/app/javascript/mastodon/features/ui/components/zoomable_image.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import IconButton from 'mastodon/components/icon_button';
 import { defineMessages, injectIntl } from 'react-intl';
@@ -92,7 +92,7 @@ const normalizeWheel = event => {
 };
 
 export default @injectIntl
-class ZoomableImage extends React.PureComponent {
+class ZoomableImage extends PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
@@ -410,7 +410,7 @@ class ZoomableImage extends React.PureComponent {
     const zoomButtonTitle = this.state.zoomState === 'compress' ? intl.formatMessage(messages.compress) : intl.formatMessage(messages.expand);
 
     return (
-      <React.Fragment>
+      <>
         <IconButton
           className={`media-modal__zoom-button ${zoomButtonShouldHide}`}
           title={zoomButtonTitle}
@@ -443,7 +443,7 @@ class ZoomableImage extends React.PureComponent {
             onMouseDown={this.handleMouseDown}
           />
         </div>
-      </React.Fragment>
+      </>
     );
   }
 
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js
index 9a901f12a652fb08ab8e008deccd8cdc2aa102ab..425e82f8e890d7ce1a8bf4635be9090f301b537e 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.js
@@ -1,5 +1,5 @@
 import classNames from 'classnames';
-import React from 'react';
+import { PureComponent } from 'react';
 import { HotKeys } from 'react-hotkeys';
 import { defineMessages, injectIntl } from 'react-intl';
 import { connect } from 'react-redux';
@@ -108,7 +108,7 @@ const keyMap = {
   openMedia: 'e',
 };
 
-class SwitchingColumnsArea extends React.PureComponent {
+class SwitchingColumnsArea extends PureComponent {
 
   static propTypes = {
     children: PropTypes.node,
@@ -116,7 +116,7 @@ class SwitchingColumnsArea extends React.PureComponent {
     mobile: PropTypes.bool,
   };
 
-  componentWillMount () {
+  UNSAFE_componentWillMount () {
     if (this.props.mobile) {
       document.body.classList.toggle('layout-single-column', true);
       document.body.classList.toggle('layout-multiple-columns', false);
@@ -204,7 +204,7 @@ class SwitchingColumnsArea extends React.PureComponent {
 export default @connect(mapStateToProps)
 @injectIntl
 @withRouter
-class UI extends React.PureComponent {
+class UI extends PureComponent {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
diff --git a/app/javascript/mastodon/features/ui/util/react_router_helpers.js b/app/javascript/mastodon/features/ui/util/react_router_helpers.js
index d452b871f78ecffbd3720553aca6f3c66af43f9e..74b6ea7f99f6123f354ca21e7ed51e019b6beb9b 100644
--- a/app/javascript/mastodon/features/ui/util/react_router_helpers.js
+++ b/app/javascript/mastodon/features/ui/util/react_router_helpers.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { Children, Component, PureComponent, cloneElement } from 'react';
 import PropTypes from 'prop-types';
 import { Switch, Route } from 'react-router-dom';
 
@@ -7,14 +7,14 @@ import BundleColumnError from '../components/bundle_column_error';
 import BundleContainer from '../containers/bundle_container';
 
 // Small wrapper to pass multiColumn to the route components
-export class WrappedSwitch extends React.PureComponent {
+export class WrappedSwitch extends PureComponent {
 
   render () {
     const { multiColumn, children } = this.props;
 
     return (
       <Switch>
-        {React.Children.map(children, child => React.cloneElement(child, { multiColumn }))}
+        {Children.map(children, child => cloneElement(child, { multiColumn }))}
       </Switch>
     );
   }
@@ -29,7 +29,7 @@ WrappedSwitch.propTypes = {
 // Small Wrapper to extract the params from the route and pass
 // them to the rendered component, together with the content to
 // be rendered inside (the children)
-export class WrappedRoute extends React.Component {
+export class WrappedRoute extends Component {
 
   static propTypes = {
     component: PropTypes.func.isRequired,
diff --git a/app/javascript/mastodon/features/ui/util/reduced_motion.js b/app/javascript/mastodon/features/ui/util/reduced_motion.js
index 95519042b43290c010809b3f62ec1d90734fb814..b33742e0f2bc5754e81c8dc8f4c9da6f2bfdbaff 100644
--- a/app/javascript/mastodon/features/ui/util/reduced_motion.js
+++ b/app/javascript/mastodon/features/ui/util/reduced_motion.js
@@ -1,6 +1,6 @@
 // Like react-motion's Motion, but reduces all animations to cross-fades
 // for the benefit of users with motion sickness.
-import React from 'react';
+import { Component } from 'react';
 import Motion from 'react-motion/lib/Motion';
 import PropTypes from 'prop-types';
 
@@ -11,7 +11,7 @@ const extractValue = (value) => {
   return (typeof value === 'object' && value && 'val' in value) ? value.val : value;
 };
 
-class ReducedMotion extends React.Component {
+class ReducedMotion extends Component {
 
   static propTypes = {
     defaultStyle: PropTypes.object,
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js
index 4f90e955fe740c7aca139d408ef65dae9f656230..d5cd94a6b22e011393c247a538080a2af2c5c395 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { is } from 'immutable';
@@ -95,7 +95,7 @@ export const fileNameFromURL = str => {
 };
 
 export default @injectIntl
-class Video extends React.PureComponent {
+class Video extends PureComponent {
 
   static propTypes = {
     preview: PropTypes.string,
@@ -391,7 +391,7 @@ class Video extends React.PureComponent {
     }
   }
 
-  componentWillReceiveProps (nextProps) {
+  UNSAFE_componentWillReceiveProps (nextProps) {
     if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
       this.setState({ revealed: nextProps.visible });
     }
diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.js
index bda51f692b770f8cda0c433648d35b085b81d0f6..2795f3e2022a8c4d10818f580cc46f053737156d 100644
--- a/app/javascript/mastodon/main.js
+++ b/app/javascript/mastodon/main.js
@@ -1,11 +1,10 @@
-import * as registerPushNotifications from './actions/push_notifications';
-import { setupBrowserNotifications } from './actions/notifications';
-import { default as Mastodon, store } from './containers/mastodon';
-import React from 'react';
-import ReactDOM from 'react-dom';
-import ready from './ready';
+import { createRoot } from 'react-dom/client';
+import * as registerPushNotifications from 'mastodon/actions/push_notifications';
+import { setupBrowserNotifications } from 'mastodon/actions/notifications';
+import { default as Mastodon, store } from 'mastodon/containers/mastodon';
+import ready from 'mastodon/ready';
 
-const perf = require('./performance');
+const perf = require('mastodon/performance');
 
 function main() {
   perf.start('main()');
@@ -20,9 +19,10 @@ function main() {
 
   ready(() => {
     const mountNode = document.getElementById('mastodon');
-    const props = JSON.parse(mountNode.getAttribute('data-props'));
+    const root      = createRoot(mountNode);
+    const props     = JSON.parse(mountNode.getAttribute('data-props'));
 
-    ReactDOM.render(<Mastodon {...props} />, mountNode);
+    root.render(<Mastodon {...props} />);
     store.dispatch(setupBrowserNotifications());
     if (process.env.NODE_ENV === 'production') {
       // avoid offline in dev mode because it's harder to debug
diff --git a/app/javascript/packs/about.js b/app/javascript/packs/about.js
index 892d825ece23e552289d47300a9d887e9949f938..7c25f99db16c838681af2046136969c1b6c75b4a 100644
--- a/app/javascript/packs/about.js
+++ b/app/javascript/packs/about.js
@@ -1,23 +1,26 @@
 import './public-path';
-import loadPolyfills from '../mastodon/load_polyfills';
-import { start } from '../mastodon/common';
+
+import loadPolyfills from 'mastodon/load_polyfills';
+import { start } from 'mastodon/common';
 
 start();
 
 function loaded() {
-  const TimelineContainer = require('../mastodon/containers/timeline_container').default;
-  const React             = require('react');
-  const ReactDOM          = require('react-dom');
-  const mountNode         = document.getElementById('mastodon-timeline');
+  const { createRoot }                 = require('react-dom/client');
+  const { default: TimelineContainer } = require('mastodon/containers/timeline_container');
+
+  const mountNode = document.getElementById('mastodon-timeline');
 
   if (mountNode !== null) {
+    const root  = createRoot(mountNode);
     const props = JSON.parse(mountNode.getAttribute('data-props'));
-    ReactDOM.render(<TimelineContainer {...props} />, mountNode);
+
+    root.render(<TimelineContainer {...props} />);
   }
 }
 
 function main() {
-  const ready = require('../mastodon/ready').default;
+  const { default: ready } = require('mastodon/ready');
   ready(loaded);
 }
 
diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js
index a3ed1ffedd8febef83a8d23f05c7a5b5d09556ca..2f6807318a77574c33abc70877832ab7e4f986d4 100644
--- a/app/javascript/packs/admin.js
+++ b/app/javascript/packs/admin.js
@@ -1,6 +1,7 @@
 import './public-path';
+
 import { delegate } from '@rails/ujs';
-import ready from '../mastodon/ready';
+import ready from 'mastodon/ready';
 
 const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]';
 
@@ -110,21 +111,23 @@ ready(() => {
     }
   });
 
-  const React    = require('react');
-  const ReactDOM = require('react-dom');
+  const { createRoot } = require('react-dom/client');
 
   [].forEach.call(document.querySelectorAll('[data-admin-component]'), element => {
     const componentName  = element.getAttribute('data-admin-component');
     const { locale, ...componentProps } = JSON.parse(element.getAttribute('data-props'));
 
-    import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => {
-      return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => {
-        ReactDOM.render((
-          <AdminComponent locale={locale}>
-            <Component {...componentProps} />
-          </AdminComponent>
-        ), element);
-      });
+    Promise.all([
+      import('mastodon/containers/admin_component'),
+      import('mastodon/components/admin/' + componentName),
+    ]).then(({ default: AdminComponent }, { default: Component }) => {
+      const root = createRoot(element);
+
+      root.render((
+        <AdminComponent locale={locale}>
+          <Component {...componentProps} />
+        </AdminComponent>
+      ));
     }).catch(error => {
       console.error(error);
     });
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index e42468e0c69938911824a54b0b1a62abf7ffd60d..f3a3f8c071a5243472be066e109b49330d274245 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -1,9 +1,10 @@
 import './public-path';
+
 import escapeTextContentForBrowser from 'escape-html';
-import loadPolyfills from '../mastodon/load_polyfills';
-import ready from '../mastodon/ready';
-import { start } from '../mastodon/common';
-import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions';
+import loadPolyfills from 'mastodon/load_polyfills';
+import ready from 'mastodon/ready';
+import { start } from 'mastodon/common';
+import loadKeyboardExtensions from 'mastodon/load_keyboard_extensions';
 import 'cocoon-js-vanilla';
 
 start();
@@ -25,16 +26,16 @@ window.addEventListener('message', e => {
 });
 
 function main() {
-  const IntlMessageFormat = require('intl-messageformat').default;
-  const { timeAgoString } = require('../mastodon/components/relative_timestamp');
-  const { delegate } = require('@rails/ujs');
-  const emojify = require('../mastodon/features/emoji/emoji').default;
-  const { getLocale } = require('../mastodon/locales');
+  const { delegate }                   = require('@rails/ujs');
+  const { createBrowserHistory }       = require('history');
+  const { default: IntlMessageFormat } = require('intl-messageformat');
+  const { createRoot }                 = require('react-dom/client');
+  const Rellax                         = require('rellax');
+  const { timeAgoString }              = require('mastodon/components/relative_timestamp');
+  const { default: emojify }           = require('mastodon/features/emoji/emoji');
+  const { getLocale }                  = require('mastodon/locales');
+
   const { messages } = getLocale();
-  const React = require('react');
-  const ReactDOM = require('react-dom');
-  const Rellax = require('rellax');
-  const { createBrowserHistory } = require('history');
 
   const scrollToDetailedStatus = () => {
     const history = createBrowserHistory();
@@ -99,8 +100,9 @@ function main() {
           });
 
           const content = document.createElement('div');
+          const root    = createRoot(content);
 
-          ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content);
+          root.render(<MediaContainer locale={locale} components={reactComponents} />);
           document.body.appendChild(content);
           scrollToDetailedStatus();
         })
diff --git a/app/javascript/packs/share.js b/app/javascript/packs/share.js
index 1225d7b52943151a18249c091a7454a63c012d79..c10e854ab888f22c468f9fd2bd09b1eb6d5bed13 100644
--- a/app/javascript/packs/share.js
+++ b/app/javascript/packs/share.js
@@ -1,23 +1,27 @@
 import './public-path';
-import loadPolyfills from '../mastodon/load_polyfills';
-import { start } from '../mastodon/common';
+
+import loadPolyfills from 'mastodon/load_polyfills';
+import { start } from 'mastodon/common';
 
 start();
 
 function loaded() {
-  const ComposeContainer = require('../mastodon/containers/compose_container').default;
-  const React = require('react');
-  const ReactDOM = require('react-dom');
+  const { createRoot }                = require('react-dom/client');
+  const { default: ComposeContainer } = require('mastodon/containers/compose_container');
+
   const mountNode = document.getElementById('mastodon-compose');
 
   if (mountNode !== null) {
+    const root  = createRoot(mountNode);
     const props = JSON.parse(mountNode.getAttribute('data-props'));
-    ReactDOM.render(<ComposeContainer {...props} />, mountNode);
+
+    root.render(<ComposeContainer {...props} />);
   }
 }
 
 function main() {
-  const ready = require('../mastodon/ready').default;
+  const { default: ready }= require('mastodon/ready');
+
   ready(loaded);
 }
 
diff --git a/babel.config.js b/babel.config.js
index b040cc159604de0b9e535bd02cf94a39001b2113..404d8614e754b973e2c933ca4a77cb53ee644277 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -3,6 +3,7 @@ module.exports = (api) => {
 
   const reactOptions = {
     development: false,
+    runtime: 'automatic',
   };
 
   const envOptions = {
diff --git a/package.json b/package.json
index c263538e969f9e4fc062309e63078e7f7f4606b8..b42e3b9ff63dee0bdf033e6141e7f954075a8f00 100644
--- a/package.json
+++ b/package.json
@@ -91,8 +91,8 @@
     "promise.prototype.finally": "^3.1.3",
     "prop-types": "^15.8.1",
     "punycode": "^2.1.0",
-    "react": "^16.14.0",
-    "react-dom": "^16.14.0",
+    "react": "^18.1.0",
+    "react-dom": "^18.1.0",
     "react-hotkeys": "^1.1.4",
     "react-immutable-proptypes": "^2.2.0",
     "react-immutable-pure-component": "^2.2.2",
@@ -101,7 +101,7 @@
     "react-motion": "^0.5.2",
     "react-notification": "^6.8.5",
     "react-overlays": "^0.9.3",
-    "react-redux": "^7.2.8",
+    "react-redux": "^8.0.1",
     "react-redux-loading-bar": "^4.0.8",
     "react-router-dom": "^4.1.1",
     "react-router-scroll-4": "^1.0.0-beta.1",
@@ -141,7 +141,7 @@
   "devDependencies": {
     "@babel/eslint-parser": "^7.18.2",
     "@testing-library/jest-dom": "^5.16.4",
-    "@testing-library/react": "^12.1.5",
+    "@testing-library/react": "^13.2.0",
     "babel-jest": "^28.1.1",
     "eslint": "^7.32.0",
     "eslint-plugin-import": "~2.26.0",
@@ -154,7 +154,7 @@
     "prettier": "^2.7.1",
     "raf": "^3.4.1",
     "react-intl-translations-manager": "^5.0.3",
-    "react-test-renderer": "^16.14.0",
+    "react-test-renderer": "^18.1.0",
     "stylelint": "^14.9.1",
     "stylelint-config-standard-scss": "^4.0.0",
     "webpack-dev-server": "^3.11.3",
diff --git a/yarn.lock b/yarn.lock
index 12b02d95c9eb2e706ed188f2b7373d217f4aca70..72765fffe684986b0860f38dfcd0d94b693eed59 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1028,7 +1028,14 @@
   dependencies:
     regenerator-runtime "^0.12.0"
 
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
+  version "7.17.9"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
+  integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
+"@babel/runtime@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580"
   integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ==
@@ -1587,17 +1594,17 @@
   dependencies:
     "@sinonjs/commons" "^1.7.0"
 
-"@testing-library/dom@^8.0.0":
-  version "8.1.0"
-  resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.1.0.tgz#f8358b1883844ea569ba76b7e94582168df5370d"
-  integrity sha512-kmW9alndr19qd6DABzQ978zKQ+J65gU2Rzkl8hriIetPnwpesRaK4//jEQyYh8fEALmGhomD/LBQqt+o+DL95Q==
+"@testing-library/dom@^8.5.0":
+  version "8.13.0"
+  resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.13.0.tgz#bc00bdd64c7d8b40841e27a70211399ad3af46f5"
+  integrity sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ==
   dependencies:
     "@babel/code-frame" "^7.10.4"
     "@babel/runtime" "^7.12.5"
     "@types/aria-query" "^4.2.0"
-    aria-query "^4.2.2"
+    aria-query "^5.0.0"
     chalk "^4.1.0"
-    dom-accessibility-api "^0.5.6"
+    dom-accessibility-api "^0.5.9"
     lz-string "^1.4.4"
     pretty-format "^27.0.2"
 
@@ -1616,14 +1623,14 @@
     lodash "^4.17.15"
     redent "^3.0.0"
 
-"@testing-library/react@^12.1.5":
-  version "12.1.5"
-  resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b"
-  integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==
+"@testing-library/react@^13.2.0":
+  version "13.2.0"
+  resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.2.0.tgz#2db00bc94d71c4e90e5c25582e90a650ae2925bf"
+  integrity sha512-Bprbz/SZVONCJy5f7hcihNCv313IJXdYiv0nSJklIs1SQCIHHNlnGNkosSXnGZTmesyGIcBGNppYhXcc11pb7g==
   dependencies:
     "@babel/runtime" "^7.12.5"
-    "@testing-library/dom" "^8.0.0"
-    "@types/react-dom" "<18.0.0"
+    "@testing-library/dom" "^8.5.0"
+    "@types/react-dom" "^18.0.0"
 
 "@tootallnate/once@2":
   version "2.0.0"
@@ -1694,7 +1701,7 @@
   dependencies:
     "@types/node" "*"
 
-"@types/hoist-non-react-statics@^3.3.0":
+"@types/hoist-non-react-statics@^3.3.1":
   version "3.3.1"
   resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
   integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
@@ -1801,22 +1808,12 @@
   resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
   integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
 
-"@types/react-dom@<18.0.0":
-  version "17.0.15"
-  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.15.tgz#f2c8efde11521a4b7991e076cb9c70ba3bb0d156"
-  integrity sha512-Tr9VU9DvNoHDWlmecmcsE5ZZiUkYx+nKBzum4Oxe1K0yJVyBlfbq7H3eXjxXqJczBKqPGq3EgfTru4MgKb9+Yw==
-  dependencies:
-    "@types/react" "^17"
-
-"@types/react-redux@^7.1.20":
-  version "7.1.20"
-  resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.20.tgz#42f0e61ababb621e12c66c96dda94c58423bd7df"
-  integrity sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==
+"@types/react-dom@^18.0.0":
+  version "18.0.3"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.3.tgz#a022ea08c75a476fe5e96b675c3e673363853831"
+  integrity sha512-1RRW9kst+67gveJRYPxGmVy8eVJ05O43hg77G2j5m76/RFJtMbcfAs2viQ2UNsvvDg8F7OfQZx8qQcl6ymygaQ==
   dependencies:
-    "@types/hoist-non-react-statics" "^3.3.0"
     "@types/react" "*"
-    hoist-non-react-statics "^3.3.0"
-    redux "^4.0.0"
 
 "@types/react-transition-group@^4.4.0":
   version "4.4.3"
@@ -1834,15 +1831,6 @@
     "@types/scheduler" "*"
     csstype "^3.0.2"
 
-"@types/react@^17":
-  version "17.0.44"
-  resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.44.tgz#c3714bd34dd551ab20b8015d9d0dbec812a51ec7"
-  integrity sha512-Ye0nlw09GeMp2Suh8qoOv0odfgCoowfM/9MG6WeRD60Gq9wS90bdkdRtYbRkNhXOpG4H+YXGvj4wOWhAC0LJ1g==
-  dependencies:
-    "@types/prop-types" "*"
-    "@types/scheduler" "*"
-    csstype "^3.0.2"
-
 "@types/scheduler@*":
   version "0.16.1"
   resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
@@ -1870,6 +1858,11 @@
   resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
   integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==
 
+"@types/use-sync-external-store@^0.0.3":
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
+  integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
+
 "@types/yargs-parser@*":
   version "15.0.0"
   resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@@ -4117,6 +4110,11 @@ dom-accessibility-api@^0.5.6:
   resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9"
   integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw==
 
+dom-accessibility-api@^0.5.9:
+  version "0.5.14"
+  resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56"
+  integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==
+
 dom-helpers@^3.2.1, dom-helpers@^3.4.0:
   version "3.4.0"
   resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
@@ -8877,7 +8875,7 @@ prop-types-extra@^1.0.1:
     react-is "^16.3.2"
     warning "^4.0.0"
 
-prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.8.1:
   version "15.8.1"
   resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
   integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -9035,15 +9033,13 @@ raw-body@2.5.1:
     iconv-lite "0.4.24"
     unpipe "1.0.0"
 
-react-dom@^16.14.0:
-  version "16.14.0"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
-  integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==
+react-dom@^18.1.0:
+  version "18.1.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.1.0.tgz#7f6dd84b706408adde05e1df575b3a024d7e8a2f"
+  integrity sha512-fU1Txz7Budmvamp7bshe4Zi32d0ll7ect+ccxNu9FlObT605GOEB8BfO4tmRJ39R5Zj831VCpvQ05QPBW5yb+w==
   dependencies:
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    prop-types "^15.6.2"
-    scheduler "^0.19.1"
+    scheduler "^0.22.0"
 
 react-event-listener@^0.6.0:
   version "0.6.6"
@@ -9105,21 +9101,21 @@ react-intl@^2.9.0:
     intl-relativeformat "^2.1.0"
     invariant "^2.1.1"
 
-react-is@^16.12.0, react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.6:
+react-is@^16.12.0, react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0:
   version "16.13.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
   integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
 
-react-is@^17.0.1, react-is@^17.0.2:
-  version "17.0.2"
-  resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
-  integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-
-react-is@^18.0.0:
+"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.1.0:
   version "18.1.0"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67"
   integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==
 
+react-is@^17.0.1:
+  version "17.0.2"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
+  integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+
 react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@@ -9170,17 +9166,17 @@ react-redux-loading-bar@^4.0.8:
     prop-types "^15.6.2"
     react-lifecycles-compat "^3.0.2"
 
-react-redux@^7.2.8:
-  version "7.2.8"
-  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"
-  integrity sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==
+react-redux@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.1.tgz#2bc029f5ada9b443107914c373a2750f6bc0f40c"
+  integrity sha512-LMZMsPY4DYdZfLJgd7i79n5Kps5N9XVLCJJeWAaPYTV+Eah2zTuBjTxKtNEbjiyitbq80/eIkm55CYSLqAub3w==
   dependencies:
-    "@babel/runtime" "^7.15.4"
-    "@types/react-redux" "^7.1.20"
+    "@babel/runtime" "^7.12.1"
+    "@types/hoist-non-react-statics" "^3.3.1"
+    "@types/use-sync-external-store" "^0.0.3"
     hoist-non-react-statics "^3.3.2"
-    loose-envify "^1.4.0"
-    prop-types "^15.7.2"
-    react-is "^17.0.2"
+    react-is "^18.0.0"
+    use-sync-external-store "^1.0.0"
 
 react-router-dom@^4.1.1:
   version "4.3.1"
@@ -9228,6 +9224,14 @@ react-select@^5.3.2:
     prop-types "^15.6.0"
     react-transition-group "^4.3.0"
 
+react-shallow-renderer@^16.15.0:
+  version "16.15.0"
+  resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
+  integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
+  dependencies:
+    object-assign "^4.1.1"
+    react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
+
 react-sparklines@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/react-sparklines/-/react-sparklines-1.7.0.tgz#9b1d97e8c8610095eeb2ad658d2e1fcf91f91a60"
@@ -9266,15 +9270,14 @@ react-swipeable-views@^0.14.0:
     react-swipeable-views-utils "^0.14.0"
     warning "^4.0.1"
 
-react-test-renderer@^16.14.0:
-  version "16.14.0"
-  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae"
-  integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==
+react-test-renderer@^18.1.0:
+  version "18.1.0"
+  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.1.0.tgz#35b75754834cf9ab517b6813db94aee0a6b545c3"
+  integrity sha512-OfuueprJFW7h69GN+kr4Ywin7stcuqaYAt1g7airM5cUgP0BoF5G5CXsPGmXeDeEkncb2fqYNECO4y18sSqphg==
   dependencies:
-    object-assign "^4.1.1"
-    prop-types "^15.6.2"
-    react-is "^16.8.6"
-    scheduler "^0.19.1"
+    react-is "^18.1.0"
+    react-shallow-renderer "^16.15.0"
+    scheduler "^0.22.0"
 
 react-textarea-autosize@^8.3.4:
   version "8.3.4"
@@ -9312,14 +9315,12 @@ react-transition-group@^4.3.0:
     loose-envify "^1.4.0"
     prop-types "^15.6.2"
 
-react@^16.14.0:
-  version "16.14.0"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
-  integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==
+react@^18.1.0:
+  version "18.1.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890"
+  integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ==
   dependencies:
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    prop-types "^15.6.2"
 
 read-pkg-up@^7.0.1:
   version "7.0.1"
@@ -9420,7 +9421,7 @@ redux-thunk@^2.4.1:
   resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
   integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==
 
-redux@^4.0.0, redux@^4.2.0:
+redux@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13"
   integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==
@@ -9761,13 +9762,12 @@ saxes@^6.0.0:
   dependencies:
     xmlchars "^2.2.0"
 
-scheduler@^0.19.1:
-  version "0.19.1"
-  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
-  integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
+scheduler@^0.22.0:
+  version "0.22.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8"
+  integrity sha512-6QAm1BgQI88NPYymgGQLCZgvep4FyePDWFpXVK+zNSUgHwlqpJy8VEh8Et0KxTACS4VWwMousBElAZOH9nkkoQ==
   dependencies:
     loose-envify "^1.1.0"
-    object-assign "^4.1.1"
 
 schema-utils@^1.0.0:
   version "1.0.0"
@@ -11158,6 +11158,11 @@ use-latest@^1.2.1:
   dependencies:
     use-isomorphic-layout-effect "^1.1.1"
 
+use-sync-external-store@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82"
+  integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ==
+
 use@^3.1.0:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"