From b3750e4fe35b74d4cb56aaccaba95d1d63b7c0c1 Mon Sep 17 00:00:00 2001 From: Linda Paiste Date: Sun, 16 Jul 2023 15:58:12 -0500 Subject: [PATCH 1/2] Convert App to a function component. --- client/modules/App/App.jsx | 98 ++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/client/modules/App/App.jsx b/client/modules/App/App.jsx index 1f41446a79..a2b4d1e7ad 100644 --- a/client/modules/App/App.jsx +++ b/client/modules/App/App.jsx @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; +import React, { useEffect, useRef, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import getConfig from '../../utils/getConfig'; import DevTools from './components/DevTools'; import { setPreviousPath } from '../IDE/actions/ide'; @@ -14,51 +14,50 @@ function hideCookieConsent(pathname) { return false; } -class App extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { isMounted: false }; - } +// TODO: get location from `useLocation` after upgrading react-router to v5. +const App = ({ children, location }) => { + const dispatch = useDispatch(); - componentDidMount() { - this.setState({ isMounted: true }); // eslint-disable-line react/no-did-mount-set-state - document.body.className = this.props.theme; - } + const theme = useSelector((state) => state.preferences.theme); + useEffect(() => { + document.body.className = theme; + }, [theme]); - componentWillReceiveProps(nextProps) { - const locationWillChange = nextProps.location !== this.props.location; - const shouldSkipRemembering = - nextProps.location.state && - nextProps.location.state.skipSavingPath === true; + // TODO: this is only needed for the initial load and would be better handled elsewhere - Linda + const language = useSelector((state) => state.preferences.language); + useEffect(() => { + dispatch(setLanguage(language, { persistPreference: false })); + }, [language]); - if (locationWillChange && !shouldSkipRemembering) { - this.props.setPreviousPath(this.props.location.pathname); - } + // TODO: do we actually need this? - Linda + const [isMounted, setIsMounted] = useState(false); + useEffect(() => setIsMounted(true), []); - if (this.props.language !== nextProps.language) { - this.props.setLanguage(nextProps.language, { persistPreference: false }); - } - } + const previousLocationRef = useRef(location); + useEffect(() => { + const prevLocation = previousLocationRef.current; + const locationChanged = + prevLocation && prevLocation.pathname !== location.pathname; + const shouldSkipRemembering = location.state?.skipSavingPath === true; - componentDidUpdate(prevProps) { - if (this.props.theme !== prevProps.theme) { - document.body.className = this.props.theme; + if (locationChanged && !shouldSkipRemembering) { + dispatch(setPreviousPath(location.pathname)); } - } + previousLocationRef.current = location; + }, [location]); - render() { - const hide = hideCookieConsent(this.props.location.pathname); - return ( -
- - {this.state.isMounted && - !window.devToolsExtension && - getConfig('NODE_ENV') === 'development' && } - {this.props.children} -
- ); - } -} + const hide = hideCookieConsent(location.pathname); + + return ( +
+ + {isMounted && + !window.devToolsExtension && + getConfig('NODE_ENV') === 'development' && } + {children} +
+ ); +}; App.propTypes = { children: PropTypes.element, @@ -67,24 +66,11 @@ App.propTypes = { state: PropTypes.shape({ skipSavingPath: PropTypes.bool }) - }).isRequired, - setPreviousPath: PropTypes.func.isRequired, - setLanguage: PropTypes.func.isRequired, - language: PropTypes.string, - theme: PropTypes.string + }).isRequired }; App.defaultProps = { - children: null, - language: null, - theme: 'light' + children: null }; -const mapStateToProps = (state) => ({ - theme: state.preferences.theme, - language: state.preferences.language -}); - -const mapDispatchToProps = { setPreviousPath, setLanguage }; - -export default connect(mapStateToProps, mapDispatchToProps)(App); +export default App; From 5618c283ac2f7c039798b4e672af5ed01d7e85d8 Mon Sep 17 00:00:00 2001 From: Linda Paiste Date: Sun, 16 Jul 2023 17:52:39 -0500 Subject: [PATCH 2/2] Fix previousPath handling. --- client/modules/App/App.jsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/modules/App/App.jsx b/client/modules/App/App.jsx index a2b4d1e7ad..777ce061ce 100644 --- a/client/modules/App/App.jsx +++ b/client/modules/App/App.jsx @@ -36,12 +36,11 @@ const App = ({ children, location }) => { const previousLocationRef = useRef(location); useEffect(() => { const prevLocation = previousLocationRef.current; - const locationChanged = - prevLocation && prevLocation.pathname !== location.pathname; + const locationChanged = prevLocation && prevLocation !== location; const shouldSkipRemembering = location.state?.skipSavingPath === true; if (locationChanged && !shouldSkipRemembering) { - dispatch(setPreviousPath(location.pathname)); + dispatch(setPreviousPath(prevLocation.pathname)); } previousLocationRef.current = location; }, [location]);