diff --git a/client/modules/App/App.jsx b/client/modules/App/App.jsx index d4d335c9e7..5de5142752 100644 --- a/client/modules/App/App.jsx +++ b/client/modules/App/App.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router-dom'; +import React, { useEffect, useRef, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useLocation } from 'react-router-dom'; import getConfig from '../../utils/getConfig'; import DevTools from './components/DevTools'; import { setPreviousPath } from '../IDE/actions/ide'; @@ -15,77 +15,57 @@ function hideCookieConsent(pathname) { return false; } -class App extends React.Component { - constructor(props, context) { - super(props, context); - this.state = { isMounted: false }; - } +const App = ({ children }) => { + const dispatch = useDispatch(); - componentDidMount() { - this.setState({ isMounted: true }); // eslint-disable-line react/no-did-mount-set-state - document.body.className = this.props.theme; - } + const location = useLocation(); - componentWillReceiveProps(nextProps) { - const locationWillChange = nextProps.location !== this.props.location; - const shouldSkipRemembering = - nextProps.location.state && - nextProps.location.state.skipSavingPath === true; + const theme = useSelector((state) => state.preferences.theme); + useEffect(() => { + document.body.className = theme; + }, [theme]); - if (locationWillChange && !shouldSkipRemembering) { - this.props.setPreviousPath(this.props.location.pathname); - } + // 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 (this.props.language !== nextProps.language) { - this.props.setLanguage(nextProps.language, { persistPreference: false }); - } - } + // TODO: do we actually need this? - Linda + const [isMounted, setIsMounted] = useState(false); + useEffect(() => setIsMounted(true), []); + + const previousLocationRef = useRef(location); + useEffect(() => { + const prevLocation = previousLocationRef.current; + const locationChanged = prevLocation && prevLocation !== location; + 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(prevLocation.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, - location: PropTypes.shape({ - pathname: PropTypes.string, - state: PropTypes.shape({ - skipSavingPath: PropTypes.bool - }) - }).isRequired, - setPreviousPath: PropTypes.func.isRequired, - setLanguage: PropTypes.func.isRequired, - language: PropTypes.string, - theme: PropTypes.string + children: PropTypes.element }; 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 withRouter(connect(mapStateToProps, mapDispatchToProps)(App)); +export default App;