From 6d5a2eebb609da67239ea37d12d6b2d3bbfef76e Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 28 Oct 2020 16:41:53 +0100 Subject: ui: Do not clutter component tree with Redux connects This change refactors the frontend to use hooks for obtaining state within the Redux store as opposed to using Higher-Order Components (HOCs). This eliminates a lot of clutter in the components. --- opendc-web/opendc-web-ui/src/pages/App.js | 168 ++++++++++--------------- opendc-web/opendc-web-ui/src/pages/Profile.js | 51 ++++---- opendc-web/opendc-web-ui/src/pages/Projects.js | 48 +++---- 3 files changed, 111 insertions(+), 156 deletions(-) (limited to 'opendc-web/opendc-web-ui/src/pages') diff --git a/opendc-web/opendc-web-ui/src/pages/App.js b/opendc-web/opendc-web-ui/src/pages/App.js index cbc805b8..03e21cc2 100644 --- a/opendc-web/opendc-web-ui/src/pages/App.js +++ b/opendc-web/opendc-web-ui/src/pages/App.js @@ -1,8 +1,8 @@ import PropTypes from 'prop-types' -import React from 'react' +import React, { useEffect } from 'react' import DocumentTitle from 'react-document-title' -import { connect } from 'react-redux' -import { ShortcutManager } from 'react-shortcuts' +import { HotKeys } from 'react-hotkeys' +import { useDispatch, useSelector } from 'react-redux' import { openPortfolioSucceeded } from '../actions/portfolios' import { openProjectSucceeded } from '../actions/projects' import ToolPanelComponent from '../components/app/map/controls/ToolPanelComponent' @@ -15,7 +15,6 @@ import DeleteRackModal from '../containers/modals/DeleteRackModal' import DeleteRoomModal from '../containers/modals/DeleteRoomModal' import EditRackNameModal from '../containers/modals/EditRackNameModal' import EditRoomNameModal from '../containers/modals/EditRoomNameModal' -import KeymapConfiguration from '../shortcuts/keymap' import NewTopologyModal from '../containers/modals/NewTopologyModal' import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' import ProjectSidebarContainer from '../containers/app/sidebars/project/ProjectSidebarContainer' @@ -23,115 +22,82 @@ import { openScenarioSucceeded } from '../actions/scenarios' import NewPortfolioModal from '../containers/modals/NewPortfolioModal' import NewScenarioModal from '../containers/modals/NewScenarioModal' import PortfolioResultsContainer from '../containers/app/results/PortfolioResultsContainer' +import KeymapConfiguration from '../shortcuts/keymap' -const shortcutManager = new ShortcutManager(KeymapConfiguration) - -class AppComponent extends React.Component { - static propTypes = { - projectId: PropTypes.string.isRequired, - portfolioId: PropTypes.string, - scenarioId: PropTypes.string, - projectName: PropTypes.string, - } - static childContextTypes = { - shortcuts: PropTypes.object.isRequired, - } +const App = ({ projectId, portfolioId, scenarioId }) => { + const projectName = useSelector( + (state) => + state.currentProjectId !== '-1' && + state.objects.project[state.currentProjectId] && + state.objects.project[state.currentProjectId].name + ) + const topologyIsLoading = useSelector((state) => state.currentTopologyId === '-1') - componentDidMount() { - if (this.props.scenarioId) { - this.props.openScenarioSucceeded(this.props.projectId, this.props.portfolioId, this.props.scenarioId) - } else if (this.props.portfolioId) { - this.props.openPortfolioSucceeded(this.props.projectId, this.props.portfolioId) + const dispatch = useDispatch() + useEffect(() => { + if (scenarioId) { + dispatch(openScenarioSucceeded(projectId, portfolioId, scenarioId)) + } else if (portfolioId) { + dispatch(openPortfolioSucceeded(projectId, portfolioId)) } else { - this.props.openProjectSucceeded(this.props.projectId) + dispatch(openProjectSucceeded(projectId)) } - } + }, [projectId, portfolioId, scenarioId, dispatch]) - getChildContext() { - return { - shortcuts: shortcutManager, - } - } + const constructionElements = topologyIsLoading ? ( +
+ +
+ ) : ( +
+ + + + + +
+ ) - render() { - const constructionElements = this.props.topologyIsLoading ? ( -
- -
- ) : ( -
- - - - - + const portfolioElements = ( +
+ +
+
- ) +
+ ) - const portfolioElements = ( -
- -
- -
+ const scenarioElements = ( +
+ +
+

Scenario loading

- ) +
+ ) - const scenarioElements = ( -
- -
-

Scenario loading

-
-
- ) - - return ( - -
- - {this.props.scenarioId - ? scenarioElements - : this.props.portfolioId - ? portfolioElements - : constructionElements} - - - - - - - - -
-
- ) - } + return ( + + + + {scenarioId ? scenarioElements : portfolioId ? portfolioElements : constructionElements} + + + + + + + + + + + ) } -const mapStateToProps = (state) => { - let projectName = undefined - if (state.currentProjectId !== '-1' && state.objects.project[state.currentProjectId]) { - projectName = state.objects.project[state.currentProjectId].name - } - - return { - topologyIsLoading: state.currentTopologyId === '-1', - projectName, - } +App.propTypes = { + projectId: PropTypes.string.isRequired, + portfolioId: PropTypes.string, + scenarioId: PropTypes.string, } -const mapDispatchToProps = (dispatch) => { - return { - openProjectSucceeded: (projectId) => dispatch(openProjectSucceeded(projectId)), - openPortfolioSucceeded: (projectId, portfolioId) => dispatch(openPortfolioSucceeded(projectId, portfolioId)), - openScenarioSucceeded: (projectId, portfolioId, scenarioId) => - dispatch(openScenarioSucceeded(projectId, portfolioId, scenarioId)), - } -} - -const App = connect(mapStateToProps, mapDispatchToProps)(AppComponent) - export default App diff --git a/opendc-web/opendc-web-ui/src/pages/Profile.js b/opendc-web/opendc-web-ui/src/pages/Profile.js index 0d94b519..1e817037 100644 --- a/opendc-web/opendc-web-ui/src/pages/Profile.js +++ b/opendc-web/opendc-web-ui/src/pages/Profile.js @@ -1,35 +1,36 @@ import React from 'react' import DocumentTitle from 'react-document-title' -import { connect } from 'react-redux' +import { useDispatch } from 'react-redux' import { openDeleteProfileModal } from '../actions/modals/profile' import DeleteProfileModal from '../containers/modals/DeleteProfileModal' import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' -const ProfileContainer = ({ onDelete }) => ( - -
- -
- -

- This does not delete your Google account, but simply disconnects it from the OpenDC platform and - deletes any project info that is associated with you (projects you own and any authorizations you - may have on other projects). -

-
- -
-
-) +const Profile = () => { + const dispatch = useDispatch() + const onDelete = () => dispatch(openDeleteProfileModal()) -const mapDispatchToProps = (dispatch) => { - return { - onDelete: () => dispatch(openDeleteProfileModal()), - } + return ( + +
+ +
+ +

+ This does not delete your Google account, but simply disconnects it from the OpenDC platform and + deletes any project info that is associated with you (projects you own and any authorizations + you may have on other projects). +

+
+ +
+
+ ) } -const Profile = connect(undefined, mapDispatchToProps)(ProfileContainer) - export default Profile diff --git a/opendc-web/opendc-web-ui/src/pages/Projects.js b/opendc-web/opendc-web-ui/src/pages/Projects.js index bb54aaa5..f759073f 100644 --- a/opendc-web/opendc-web-ui/src/pages/Projects.js +++ b/opendc-web/opendc-web-ui/src/pages/Projects.js @@ -1,7 +1,6 @@ -import React from 'react' +import React, { useEffect } from 'react' import DocumentTitle from 'react-document-title' -import { connect } from 'react-redux' -import { openNewProjectModal } from '../actions/modals/projects' +import { useDispatch } from 'react-redux' import { fetchAuthorizationsOfCurrentUser } from '../actions/users' import ProjectFilterPanel from '../components/projects/FilterPanel' import NewProjectModal from '../containers/modals/NewProjectModal' @@ -9,35 +8,24 @@ import NewProjectButtonContainer from '../containers/projects/NewProjectButtonCo import VisibleProjectList from '../containers/projects/VisibleProjectAuthList' import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' -class ProjectsContainer extends React.Component { - componentDidMount() { - this.props.fetchAuthorizationsOfCurrentUser() - } +function Projects() { + const dispatch = useDispatch() - render() { - return ( - -
- -
- - - -
- -
-
- ) - } -} + useEffect(() => dispatch(fetchAuthorizationsOfCurrentUser())) -const mapDispatchToProps = (dispatch) => { - return { - fetchAuthorizationsOfCurrentUser: () => dispatch(fetchAuthorizationsOfCurrentUser()), - openNewProjectModal: () => dispatch(openNewProjectModal()), - } + return ( + +
+ +
+ + + +
+ +
+
+ ) } -const Projects = connect(undefined, mapDispatchToProps)(ProjectsContainer) - export default Projects -- cgit v1.2.3 From c2c5dfe0119546935118ce5ae1803bf87f0b787c Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 10 May 2021 17:31:43 +0200 Subject: ui: Update React dependencies This change updates the React dependencies to version 17, in order to keep up to date with React. --- opendc-web/opendc-web-ui/src/pages/App.js | 30 +++++++++++----------- opendc-web/opendc-web-ui/src/pages/Home.js | 4 +-- opendc-web/opendc-web-ui/src/pages/NotFound.js | 11 ++++---- opendc-web/opendc-web-ui/src/pages/Profile.js | 35 +++++++++++--------------- opendc-web/opendc-web-ui/src/pages/Projects.js | 21 ++++++++-------- 5 files changed, 48 insertions(+), 53 deletions(-) (limited to 'opendc-web/opendc-web-ui/src/pages') diff --git a/opendc-web/opendc-web-ui/src/pages/App.js b/opendc-web/opendc-web-ui/src/pages/App.js index 03e21cc2..ea62e8dc 100644 --- a/opendc-web/opendc-web-ui/src/pages/App.js +++ b/opendc-web/opendc-web-ui/src/pages/App.js @@ -1,6 +1,5 @@ import PropTypes from 'prop-types' import React, { useEffect } from 'react' -import DocumentTitle from 'react-document-title' import { HotKeys } from 'react-hotkeys' import { useDispatch, useSelector } from 'react-redux' import { openPortfolioSucceeded } from '../actions/portfolios' @@ -23,6 +22,7 @@ import NewPortfolioModal from '../containers/modals/NewPortfolioModal' import NewScenarioModal from '../containers/modals/NewScenarioModal' import PortfolioResultsContainer from '../containers/app/results/PortfolioResultsContainer' import KeymapConfiguration from '../shortcuts/keymap' +import { useDocumentTitle } from '../util/hooks' const App = ({ projectId, portfolioId, scenarioId }) => { const projectName = useSelector( @@ -76,21 +76,21 @@ const App = ({ projectId, portfolioId, scenarioId }) => {
) + useDocumentTitle(projectName ? projectName + ' - OpenDC' : 'Simulation - OpenDC') + return ( - - - - {scenarioId ? scenarioElements : portfolioId ? portfolioElements : constructionElements} - - - - - - - - - - + + + {scenarioId ? scenarioElements : portfolioId ? portfolioElements : constructionElements} + + + + + + + + + ) } diff --git a/opendc-web/opendc-web-ui/src/pages/Home.js b/opendc-web/opendc-web-ui/src/pages/Home.js index 6fc940c0..fb383426 100644 --- a/opendc-web/opendc-web-ui/src/pages/Home.js +++ b/opendc-web/opendc-web-ui/src/pages/Home.js @@ -1,5 +1,4 @@ import React from 'react' -import DocumentTitle from 'react-document-title' import ContactSection from '../components/home/ContactSection' import IntroSection from '../components/home/IntroSection' import JumbotronHeader from '../components/home/JumbotronHeader' @@ -10,8 +9,10 @@ import TeamSection from '../components/home/TeamSection' import TechnologiesSection from '../components/home/TechnologiesSection' import HomeNavbar from '../components/navigation/HomeNavbar' import './Home.sass' +import { useDocumentTitle } from '../util/hooks' function Home() { + useDocumentTitle('OpenDC') return (
@@ -24,7 +25,6 @@ function Home() { -
) diff --git a/opendc-web/opendc-web-ui/src/pages/NotFound.js b/opendc-web/opendc-web-ui/src/pages/NotFound.js index 72be7342..b933ffa5 100644 --- a/opendc-web/opendc-web-ui/src/pages/NotFound.js +++ b/opendc-web/opendc-web-ui/src/pages/NotFound.js @@ -1,14 +1,15 @@ import React from 'react' -import DocumentTitle from 'react-document-title' import TerminalWindow from '../components/not-found/TerminalWindow' import './NotFound.sass' +import { useDocumentTitle } from '../util/hooks' -const NotFound = () => ( - +const NotFound = () => { + useDocumentTitle('Page Not Found - OpenDC') + return (
-
-) + ) +} export default NotFound diff --git a/opendc-web/opendc-web-ui/src/pages/Profile.js b/opendc-web/opendc-web-ui/src/pages/Profile.js index 1e817037..ea781686 100644 --- a/opendc-web/opendc-web-ui/src/pages/Profile.js +++ b/opendc-web/opendc-web-ui/src/pages/Profile.js @@ -1,35 +1,30 @@ import React from 'react' -import DocumentTitle from 'react-document-title' import { useDispatch } from 'react-redux' import { openDeleteProfileModal } from '../actions/modals/profile' import DeleteProfileModal from '../containers/modals/DeleteProfileModal' import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' +import { useDocumentTitle } from '../util/hooks' const Profile = () => { const dispatch = useDispatch() const onDelete = () => dispatch(openDeleteProfileModal()) + useDocumentTitle('My Profile - OpenDC') return ( - -
- -
- -

- This does not delete your Google account, but simply disconnects it from the OpenDC platform and - deletes any project info that is associated with you (projects you own and any authorizations - you may have on other projects). -

-
- +
+ +
+ +

+ This does not delete your Google account, but simply disconnects it from the OpenDC platform and + deletes any project info that is associated with you (projects you own and any authorizations you + may have on other projects). +

- + +
) } diff --git a/opendc-web/opendc-web-ui/src/pages/Projects.js b/opendc-web/opendc-web-ui/src/pages/Projects.js index f759073f..5e642a03 100644 --- a/opendc-web/opendc-web-ui/src/pages/Projects.js +++ b/opendc-web/opendc-web-ui/src/pages/Projects.js @@ -1,5 +1,4 @@ import React, { useEffect } from 'react' -import DocumentTitle from 'react-document-title' import { useDispatch } from 'react-redux' import { fetchAuthorizationsOfCurrentUser } from '../actions/users' import ProjectFilterPanel from '../components/projects/FilterPanel' @@ -7,24 +6,24 @@ import NewProjectModal from '../containers/modals/NewProjectModal' import NewProjectButtonContainer from '../containers/projects/NewProjectButtonContainer' import VisibleProjectList from '../containers/projects/VisibleProjectAuthList' import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' +import { useDocumentTitle } from '../util/hooks' function Projects() { const dispatch = useDispatch() useEffect(() => dispatch(fetchAuthorizationsOfCurrentUser())) + useDocumentTitle('My Projects - OpenDC') return ( - -
- -
- - - -
- +
+ +
+ + +
- + +
) } -- cgit v1.2.3