diff --git a/app/components/AdminPage.js b/app/components/AdminPage.js
index 8a73fa9cc4141e5cb27153da4c9e34ec1e304e92..19c02ee7cc74b038f33781080431b0fa1b1c779a 100644
--- a/app/components/AdminPage.js
+++ b/app/components/AdminPage.js
@@ -1,15 +1,13 @@
 import React, { useContext, useCallback, useRef } from 'react'
 import styled from 'styled-components'
-import { compose } from 'recompose'
 import { useQuery } from '@apollo/client'
 import {
-  withRouter,
+  useHistory,
   matchPath,
   Route,
   Switch,
   Redirect,
 } from 'react-router-dom'
-// import { Action } from '@pubsweet/ui'
 import { JournalContext } from './xpub-journal/src'
 import { XpubContext } from './xpub-with-context/src'
 
@@ -75,7 +73,8 @@ const updateStuff = data => {
   }
 }
 
-const AdminPage = ({ children, history, match }) => {
+const AdminPage = () => {
+  const history = useHistory()
   const journal = useContext(JournalContext)
   const [conversion] = useContext(XpubContext)
 
@@ -205,4 +204,4 @@ const AdminPage = ({ children, history, match }) => {
   )
 }
 
-export default compose(withRouter)(AdminPage)
+export default AdminPage
diff --git a/app/components/Menu.js b/app/components/Menu.js
index 45963e4444671966ea218f2ae0183195731e8662..8c39a1ede00ec449d3d2d46ddd631ad32a96fcc6 100644
--- a/app/components/Menu.js
+++ b/app/components/Menu.js
@@ -2,7 +2,7 @@ import React from 'react'
 import styled, { css } from 'styled-components'
 // import PropTypes from 'prop-types'
 import { th, grid, lighten } from '@pubsweet/ui-toolkit'
-import { Link } from 'react-router-dom'
+import { Link, useLocation } from 'react-router-dom'
 import { Icon } from '@pubsweet/ui'
 import { UserAvatar } from '../components/component-avatar/src'
 
@@ -116,23 +116,26 @@ const Menu = ({
   navLinkComponents,
   user,
   notice,
-}) => (
-  <Root className={className}>
-    <Section>
-      {/* TODO: Place this notice (used for offline notification) better */}
-      {notice}
-      <UserComponent loginLink={loginLink} user={user} />
-      {navLinkComponents &&
-        navLinkComponents.map((navInfo, idx) => (
-          <Item
-            {...navInfo}
-            active={window.location.pathname === navInfo.link}
-            key={navInfo.link}
-          />
-        ))}
-    </Section>
-  </Root>
-)
+}) => {
+  const location = useLocation()
+  return (
+    <Root className={className}>
+      <Section>
+        {/* TODO: Place this notice (used for offline notification) better */}
+        {notice}
+        <UserComponent loginLink={loginLink} user={user} />
+        {navLinkComponents &&
+          navLinkComponents.map((navInfo, idx) => (
+            <Item
+              {...navInfo}
+              active={location.pathname === navInfo.link}
+              key={navInfo.link}
+            />
+          ))}
+      </Section>
+    </Root>
+  )
+}
 
 const UserComponent = ({ user, loginLink }) => (
   <Section>
diff --git a/app/components/component-formbuilder/src/components/ComponentProperties.jsx b/app/components/component-formbuilder/src/components/ComponentProperties.jsx
index 5c079e05da5ddab7f11dcbbf45fd9a00e77b6629..33397de0ee3095017896d050c806d38cc83921ca 100644
--- a/app/components/component-formbuilder/src/components/ComponentProperties.jsx
+++ b/app/components/component-formbuilder/src/components/ComponentProperties.jsx
@@ -1,16 +1,7 @@
 import React, { useState } from 'react'
 import { map, omitBy } from 'lodash'
-// import {
-//   branch,
-//   renderComponent,
-//   compose,
-//   withState,
-//   withHandlers,
-//   withProps,
-// } from 'recompose'
 import { ValidatedFieldFormik, Menu, Button } from '@pubsweet/ui'
 import { Formik } from 'formik'
-
 import FormProperties from './FormProperties'
 import components from './config/Elements'
 import * as elements from './builderComponents'
diff --git a/app/components/component-formbuilder/src/components/FormBuilderPage.js b/app/components/component-formbuilder/src/components/FormBuilderPage.js
index 17b795534b8ab540cc3808a79cbd9c7d00d3af0b..07b2d938327d009dcfdf19cdeaacecbe49b2cc57 100644
--- a/app/components/component-formbuilder/src/components/FormBuilderPage.js
+++ b/app/components/component-formbuilder/src/components/FormBuilderPage.js
@@ -1,9 +1,5 @@
 import React, { useState, useEffect } from 'react'
-// import { compose, withState, withHandlers, withProps } from 'recompose'
 import { useQuery, useMutation, gql } from '@apollo/client'
-// import gql from 'graphql-tag'
-// import { withLoader } from 'pubsweet-client'
-
 import FormBuilderLayout from './FormBuilderLayout'
 import { Spinner } from '../../../shared'
 
@@ -93,81 +89,3 @@ const FormBuilderPage = props => {
 }
 
 export default FormBuilderPage
-
-// export default compose(
-// graphql(query),
-// graphql(deleteForms, {
-//   name: 'deleteForms',
-// }),
-// graphql(deleteFormElement, {
-//   name: 'deleteFormElement',
-// }),
-// graphql(updateForm, {
-//   name: 'updateForm',
-// }),
-// graphql(createForm, {
-//   name: 'createForm',
-// }),
-// graphql(updateFormElements, {
-//   name: 'updateFormElements',
-// }),
-// withLoader(),
-// withProps(props => ({
-//   deleteForm: formId =>
-//     props.deleteForms({
-//       variables: {
-//         formId,
-//       },
-//     }),
-//   deleteElement: (formId, elementId) =>
-//     props.deleteFormElement({
-//       variables: {
-//         formId,
-//         elementId,
-//       },
-//     }),
-//   updateForm: (form, formProperties) =>
-//     props.updateForm({
-//       variables: {
-//         form: JSON.stringify(formProperties),
-//         id: form.id,
-//       },
-//     }),
-//   createForm: formProperties =>
-//     props.createForm({
-//       variables: {
-//         form: JSON.stringify(formProperties),
-//       },
-//     }),
-//   updateElements: (form, formElements) =>
-//     props.updateFormElements({
-//       variables: {
-//         form: JSON.stringify(formElements),
-//         formId: form.id,
-//       },
-//     }),
-// })),
-// withState('properties', 'onChangeProperties', ({ getForms }) => ({
-//   type: 'form',
-//   properties: getForms[0] || {},
-// })),
-// withState('activeTab', 'onChangeTab', ({ getForms, activeTab }) =>
-//   getForms.length === 0 ? 'new' : 0,
-// ),
-//   withHandlers({
-//     changeProperties: ({
-//       onChangeProperties,
-//       getForms,
-//       activeTab,
-//     }) => properties =>
-//       onChangeProperties(
-//         () =>
-//           Object.assign({}, properties, {
-//             id: (getForms[activeTab] || {}).id,
-//           }) || undefined,
-//       ),
-//     changeTabs: ({ onChangeTab }) => activeTab => {
-//       onChangeTab(() => activeTab || '')
-//     },
-//   }),
-// )(FormBuilderLayout)
diff --git a/app/components/component-formbuilder/src/components/FormProperties.jsx b/app/components/component-formbuilder/src/components/FormProperties.jsx
index 5aa9f40f8d31ddfacbd996c21bb9b7093bd45939..84a2b1fdabc5bd9459e3019998c126dc4d3d8b0e 100644
--- a/app/components/component-formbuilder/src/components/FormProperties.jsx
+++ b/app/components/component-formbuilder/src/components/FormProperties.jsx
@@ -1,7 +1,6 @@
 import React, { useState } from 'react'
 import { isEmpty } from 'lodash'
 import styled from 'styled-components'
-// import { compose, withProps, withState, withHandlers } from 'recompose'
 import { Button, TextField, ValidatedFieldFormik } from '@pubsweet/ui'
 import { th } from '@pubsweet/ui-toolkit'
 import { AbstractField, RadioBox } from './builderComponents'
@@ -21,14 +20,6 @@ export const Section = styled.div`
   margin: calc(${th('gridUnit')} * 6) 0;
 `
 
-// const onSubmit = (values, { handleSubmit, mode }) => {
-//   if (mode === 'create') {
-//     handleSubmit(Object.assign({}, values))
-//   } else {
-//     handleSubmit(values)
-//   }
-// }
-
 const FormProperties = ({
   handleSubmit,
   properties,
diff --git a/app/components/component-review/src/components/assignEditors/AssignEditor.js b/app/components/component-review/src/components/assignEditors/AssignEditor.js
index d90c4328bc0ca22991ca52039ac5abbdfb945167..f3fb20ce821bc9f77155dfbc4ce4087cb8deaf89 100644
--- a/app/components/component-review/src/components/assignEditors/AssignEditor.js
+++ b/app/components/component-review/src/components/assignEditors/AssignEditor.js
@@ -1,10 +1,8 @@
 import React from 'react'
 import config from 'config'
-import { compose, withProps } from 'recompose'
-import { cloneDeep, get } from 'lodash'
-import { graphql } from '@apollo/client/react/hoc'
+import { get } from 'lodash'
+import { useQuery, useMutation } from '@apollo/client'
 import gql from 'graphql-tag'
-import { withLoader } from 'pubsweet-client'
 import { Select } from '../../../../shared'
 
 const editorOption = user => ({
@@ -42,7 +40,7 @@ const query = gql`
   }
 `
 
-const updateTeam = gql`
+const updateTeamMutation = gql`
   mutation($id: ID!, $input: TeamInput) {
     updateTeam(id: $id, input: $input) {
       ${teamFields}
@@ -58,92 +56,100 @@ const createTeamMutation = gql`
   }
 `
 
-// TODO: select multiple editors
-const AssignEditor = ({
-  updateTeam,
-  createTeam,
-  teamName,
-  teamRole,
-  value,
-  options,
-}) => (
-  <Select
-    aria-label={`Assign ${teamRole}`}
-    data-testid={`assign${teamRole}`}
-    label={teamName}
-    onChange={selected => {
-      // selected is { label, value } object
-      if (value) {
-        updateTeam(selected.value, teamRole)
-      } else {
-        createTeam(selected.value, teamRole)
-      }
-    }}
-    options={options}
-    placeholder={`Assign ${teamName}…`}
-    value={value}
-  />
-)
-
-export default compose(
-  graphql(query),
-  graphql(updateTeam, {
-    props: ({ mutate, ownProps }) => {
-      const updateTeam = (userId, teamRole) => {
-        const team = cloneDeep(ownProps.manuscript.teams).find(
-          team => team.role === teamRole,
-        )
-        mutate({
-          variables: {
-            id: team.id,
-            input: {
-              members: [{ user: { id: userId } }],
-            },
-          },
-        })
-      }
+const AssignEditor = ({ teamRole, manuscript }) => {
+  const team =
+    (manuscript.teams || []).find(team => team.role === teamRole) || {}
 
-      return {
-        updateTeam,
-      }
-    },
-  }),
-  graphql(createTeamMutation, {
-    props: ({ mutate, ownProps }) => {
-      const createTeam = (userId, teamRole) => {
-        const input = {
-          manuscriptId: ownProps.manuscript.id,
-          name:
-            teamRole === 'seniorEditor' ? 'Senior Editor' : 'Handling Editor',
-          role: teamRole,
-          members: [{ user: { id: userId } }],
-        }
-
-        mutate({
-          variables: {
-            input,
+  const members = team.members || []
+  const value = members.length > 0 ? members[0].user.id : undefined
+  const teamName = get(config, `authsome.teams.${teamRole}.name`)
+
+  const { data, loading, error } = useQuery(query)
+
+  const [updateTeam] = useMutation(updateTeamMutation)
+  const [createTeam] = useMutation(createTeamMutation)
+
+  if (loading || error) {
+    return null
+  }
+
+  const options = (data.users || []).map(user => editorOption(user))
+
+  const assignRole = async (userId, role) => {
+    if (value) {
+      const team = manuscript.teams.find(team => team.role === teamRole)
+      updateTeam({
+        variables: {
+          id: team.id,
+          input: {
+            members: [{ user: { id: userId } }],
           },
-        })
+        },
+      })
+    } else {
+      const input = {
+        manuscriptId: manuscript.id,
+        name: teamRole === 'seniorEditor' ? 'Senior Editor' : 'Handling Editor',
+        role: teamRole,
+        members: [{ user: { id: userId } }],
       }
 
-      return {
-        createTeam,
-      }
-    },
-  }),
-  withProps(({ teamRole, manuscript, data = {} }) => {
-    const optionUsers = (data.users || []).map(user => editorOption(user))
-
-    const team =
-      (manuscript.teams || []).find(team => team.role === teamRole) || {}
-
-    const members = team.members || []
-    const teamName = get(config, `authsome.teams.${teamRole}.name`)
-    return {
-      teamName,
-      options: optionUsers,
-      value: members.length > 0 ? members[0].user.id : undefined,
+      createTeam({
+        variables: {
+          input,
+        },
+      })
     }
-  }),
-  withLoader(),
-)(AssignEditor)
+  }
+
+  return (
+    <Select
+      aria-label={`Assign ${teamRole}`}
+      data-testid={`assign${teamRole}`}
+      label={teamName}
+      onChange={selected => assignRole(selected.value, teamRole)}
+      options={options}
+      placeholder={`Assign ${teamName}…`}
+      value={value}
+    />
+  )
+}
+
+export default AssignEditor
+// export default compose(
+// graphql(query),
+// graphql(updateTeam, {
+//   props: ({ mutate, ownProps }) => {
+//     const updateTeam = (userId, teamRole) => {}
+
+//     return {
+//       updateTeam,
+//     }
+//   },
+// }),
+// graphql(createTeamMutation, {
+//   props: ({ mutate, ownProps }) => {
+//     const createTeam = (userId, teamRole) => {
+//       const input = {
+//         manuscriptId: ownProps.manuscript.id,
+//         name:
+//           teamRole === 'seniorEditor' ? 'Senior Editor' : 'Handling Editor',
+//         role: teamRole,
+//         members: [{ user: { id: userId } }],
+//       }
+
+//       mutate({
+//         variables: {
+//           input,
+//         },
+//       })
+//     }
+
+//     return {
+//       createTeam,
+//     }
+//   },
+// }),
+
+// withLoader(),
+// )(AssignEditor)
diff --git a/app/components/component-review/src/components/decision/DecisionReview.js b/app/components/component-review/src/components/decision/DecisionReview.js
index 994e989f6f04343e3b841263cda4559bb3b11db4..82b39bcf987cf239aba36786b4a3f0dc47c2fe91 100644
--- a/app/components/component-review/src/components/decision/DecisionReview.js
+++ b/app/components/component-review/src/components/decision/DecisionReview.js
@@ -1,6 +1,5 @@
-import React, { useContext } from 'react'
+import React, { useContext, useState } from 'react'
 import styled from 'styled-components'
-import { compose, withState, withHandlers } from 'recompose'
 import { Button } from '@pubsweet/ui'
 import { th } from '@pubsweet/ui-toolkit'
 import { JournalContext } from '../../../../xpub-journal/src'
@@ -63,7 +62,7 @@ const ReviewHeading = ({
   )
 }
 
-const DecisionReview = ({ review, reviewer, open, toggleOpen }) => {
+const DecisionReview = ({ review, reviewer }) => {
   const { recommendation } = review
   const { name, ordinal } = reviewer
 
@@ -76,6 +75,10 @@ const DecisionReview = ({ review, reviewer, open, toggleOpen }) => {
   `
 
   const journal = useContext(JournalContext)
+
+  const [open, setOpen] = useState(false)
+  const toggleOpen = setOpen(!open)
+
   return (
     <Root>
       <ReviewHeading
@@ -96,11 +99,4 @@ const DecisionReview = ({ review, reviewer, open, toggleOpen }) => {
   )
 }
 
-export default compose(
-  withState('open', 'setOpen', ({ open }) => open),
-  withHandlers({
-    toggleOpen: props => () => {
-      props.setOpen(open => !open)
-    },
-  }),
-)(DecisionReview)
+export default DecisionReview
diff --git a/app/components/component-review/src/components/reviewers/ReviewerContainer.js b/app/components/component-review/src/components/reviewers/ReviewerContainer.js
index 89b9a93c1b04a6bfc6d6e6e4deb6f483cd7ce599..e6840cf8c12705126cc2242b71fb976f594c6c86 100644
--- a/app/components/component-review/src/components/reviewers/ReviewerContainer.js
+++ b/app/components/component-review/src/components/reviewers/ReviewerContainer.js
@@ -1,23 +1,3 @@
-// import { compose, withHandlers } from 'recompose'
-// import { connect } from 'react-redux'
-// import { actions } from 'pubsweet-client'
 import Reviewer from './Reviewer'
 
-// const removeReviewer = props => () => {
-//   const { id } = props.reviewer
-
-//   return props.deleteFragment(props.project, { id })
-// }
-
 export default Reviewer
-// export default compose(
-//   connect(
-//     null,
-//     {
-//       deleteFragment: actions.deleteFragment,
-//     },
-//   ),
-//   withHandlers({
-//     removeReviewer: props => removeReviewer(props),
-//   }),
-// )(Reviewer)
diff --git a/app/components/component-submit/src/components/molecules/Accordion.js b/app/components/component-submit/src/components/molecules/Accordion.js
index 3206bfc51d077a92c7f0ca6df8a012132cff0ffe..a0925a1a75cc0c9abc7c790dfa09f866c8654fc0 100644
--- a/app/components/component-submit/src/components/molecules/Accordion.js
+++ b/app/components/component-submit/src/components/molecules/Accordion.js
@@ -1,6 +1,5 @@
-import React from 'react'
+import React, { useState } from 'react'
 import styled from 'styled-components'
-import { compose, withState, withHandlers } from 'recompose'
 import { Button } from '@pubsweet/ui'
 import { th } from '@pubsweet/ui-toolkit'
 import { JournalContext } from '../../../../xpub-journal/src'
@@ -96,38 +95,28 @@ const AccordionHeading = ({
   )
 }
 
-const Accordion = ({
-  open,
-  ordinal,
-  toggleOpen,
-  title,
-  status,
-  Component,
-  withDots,
-}) => (
-  <Root>
-    <JournalContext.Consumer>
-      {journal => (
-        <AccordionHeading
-          journal={journal}
-          name={title}
-          open={open}
-          ordinal={ordinal}
-          recommendation={status}
-          toggleOpen={toggleOpen}
-          withDots={withDots || false}
-        />
-      )}
-    </JournalContext.Consumer>
-    {open && <AccordionBody>{Component}</AccordionBody>}
-  </Root>
-)
+const Accordion = ({ ordinal, title, status, Component, withDots }) => {
+  const [open, setOpen] = useState(false)
+  const toggleOpen = setOpen(!open)
+
+  return (
+    <Root>
+      <JournalContext.Consumer>
+        {journal => (
+          <AccordionHeading
+            journal={journal}
+            name={title}
+            open={open}
+            ordinal={ordinal}
+            recommendation={status}
+            toggleOpen={toggleOpen}
+            withDots={withDots || false}
+          />
+        )}
+      </JournalContext.Consumer>
+      {open && <AccordionBody>{Component}</AccordionBody>}
+    </Root>
+  )
+}
 
-export default compose(
-  withState('open', 'setOpen', ({ open }) => open || true),
-  withHandlers({
-    toggleOpen: props => () => {
-      props.setOpen(open => !open)
-    },
-  }),
-)(Accordion)
+export default Accordion
diff --git a/package.json b/package.json
index acd19898ef28f21e18c6bd9bc0e4420a783f14c8..6636058c4c5ffcf27566aae07bbfbe0c778c69fd 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,6 @@
     "react-router-dom": "5.2.0",
     "react-select1": "npm:react-select@1.3.0",
     "react-visibility-sensor": "5.1.1",
-    "recompose": "0.30.0",
     "styled-components": "4.4.1",
     "supertest": "3.4.2",
     "wax-prosemirror-core": "0.0.10",
diff --git a/yarn.lock b/yarn.lock
index f7b03452d918db166c9ed4b1775e2f4530f6e7da..71c5c877b7d83f32b1aaf635de690ca54e313e7f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14140,7 +14140,7 @@ rechoir@^0.6.2:
   dependencies:
     resolve "^1.1.6"
 
-recompose@0.30.0, recompose@^0.30.0:
+recompose@^0.30.0:
   version "0.30.0"
   resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0"
   integrity sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==