Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Context Folder hiarchy / architecture?

I'm currently looking at implementing Context into one of our apps over Redux, but, I can't seem to find any information on what would be the best structure for large scale apps?

Redux has a defined way to create reducers, actions, etc. With Context, all I've found are the generic "create a provider, put state and methods all on the same file, and then use a consumer".

TL;DR Is there a way to build a hiarchy that is beneficial for long term, and large scale applications with React Context?

Edit: I guess this is incorrect to think of them having a similar structured relationship. Unfortunately, I'm not able to use Redux because of AEM's limitations. Context does work however, so I wanted to hopefully be able to build some structure with that.

like image 950
Bryce Snyder Avatar asked Nov 21 '18 13:11

Bryce Snyder


1 Answers

First of all, I don't think there is necessarily a right or wrong answer to this question, but I will just give you my two cents.

I am currently refactoring a web application which serves several millions of sessions per month and am testing a redux and context version on internal stage servers.

Important notices:

  • I am using a mono-store approach
  • It's not an app which constantly has global store updates

To the folder structure. I like to keep my store in the root of the project. For a react app based on react-create-react-app that would be the /src and it basically consists of the following files:

  • index.js // everything gets "bundled" here
  • initialState.js // provides the store with intial state e.g. from server, cache etc.
  • methods/*.js // contains split methods based on the part of the app that they are used in (if it can be split into separate parts)

Ergo my index.js is as simple as:

import React from 'react';
import storeMethods from './methods';
import initialState from './initialState';

// to start of experimenting with context
// i would keep all read and write key value
// pairs right here and split as the codebase
// grows and you realize you need more space
export const store = {
  ...initialState,
  ...storeMethods
}

export const StoreContext = React.createContext(store)

storeMethods is a bundled export from all methods in the methods/ folder. Basically it's just another object of containing keys which values are functions like so:

export const methods = {
   showNavBar: function() {
      this.setState({ navBarOpen: true })
   }
}

initialState is as much as the representation of key value pairs that are required to render the base content of the app and or never change. Basically some global settings. Initialstate coming from the server, is being added to the store in the constructor of my App, right before I bind the lexical scope.

The store get's thrown into the state of the relevant outermost React Component and is used as the app state, where I bind the store's scope to the React Components lexical scope.

Then I have a higher order component withContextConsumer which is used to wrap any React component which needs access to the state. The HOC distributes the subscribed keys down as props to the wrapped component and can be consumed as read only or write.

No matter how you end up using Context, don't forget, that any Consumer will have it's render method automatically called if the Context Store is being updated. To avoid that on a simple oldProps !== newProps level, you can use PureComponents. For more complex diffs you can use the lifecyclemethod shouldComponentUpdate

edit

Basic App Structure

App.js:

import React, { PureComponent } from 'react'
import { StoreContext, store } from './store'
import { bindScopeToFunction } from './helpers'

class App extends PureComponent {
   constructor(props) {
      super(props)
      const { initialState = {} } = props
      const boundStore = bindScopeToFunction(store, this)
      this.state = {...boundStore, ...initialState}
   }
   render () {
      return(
         <StoreContext.Provider value={this.state}>
            // in here you render all your app
            // routing, childcomponents etc
            // in any component where you need access
            // to the global store
            // wrap it in <StoreContext.Consumer> it has
            // the whole store as render prop
         </StoreContext.Provider>
      )
   }
}

Working basic example can be found here https://codesandbox.io/s/pm85w4y6xm

like image 142
noa-dev Avatar answered Oct 07 '22 21:10

noa-dev