React - easy solution for global state or antipattern?

Viewed 203

In my React app I have main component that contians the whole state of the application and also functions used to modify the state.

In order to get access to the state and the functions in all subcomponents I'm passing this from the main component through subcomponent attributes (app):

class App extends React.Component {

  state = DefaultState;
  funcs = getController(this);

  render() {
    return (
      <Header app={this} />
      <Page app={this} />
      <Footer app={this} />
    )
  }
}


function getController(app: App) {
  return {
    switchPage: (page: string) => {
      app.setState({ page: page });
    },
  }
}

Therefore in subcomponents I can access the state variables and modify them like this:

const HeaderComponent: React.FC<CompProps> = (props) => {
  return (
    <div>
      <h1>{props.app.state.currentPage}</h1>
      <button onClick={(e) => {props.app.funcs.switchPage('home')}}>Home</button>
    </div>
  )
}

This is the quickest/simplest solution I've found for global state. However I've never seen this in any tutorial or so. I guess main problem is the whole app will rerender when a single value is changed in global state, but the same goes for React Context API.

Question is that are there any disadvantages of this approach or a reason to not use this?

1 Answers

You can save func to an exported let variable and utilize the most recent version of func without re-rendering, but as this isn't a common occurrence, you won't find much information about it. Since it's simply javascript, any known hack will work. Also, the part of your question regarding react-context re-rendering is correct although you must consider that it will re-render and it will be more pruned for optimization of unmodified siblings.

You may alternatively supply a simple ref (useRef) to those components, which will allow them to access the most recent version of func, but because the ref reference itself does not change when the page is re-rendered, they will not be updated for function change.

I'm using react functional component but the class base may be so similar

export let funcs = null

const App = () => {
  funcs = getController();
  render() {
    return (
      <Header />
      <Page />
      <Footer />
    )
  }
}

// header component
import { funcs as appFuncs } from '~/app.js'
const HeaderComponent: React.FC<CompProps> = (props) => {
  return (
    <div>
      {/* same thing can be happened for the state */}
      <button onClick={(e) => {appFuncs.switchPage('home')}}>Home</button>
    </div>
  )
}

Hooks version

const App = () => {
  const ref = useRef();
  funcs = getController();
  ref.current = {state, funcs};

  // note ref.current changes not the ref itself
  render() {
    return (
      <Header app={ref} />
      <Page app={ref} />
      <Footer app={ref} />
    )
  }
}

// header
const HeaderComponent: React.FC<CompProps> = (props) => {
  return (
    <div>
      <h1>{props.app.current.state.currentPage}</h1>
      <button onClick={(e) => {props.app.current.func.switchPage('home')}}>Home</button>
    </div>
  )
}

Any suggestions or other techniques will be much appreciated.

Related