ReactContext
This module enables to quickly and simply enable access to a React context, using the new React API.
If you are using a version of React that doesn't have the createContext
function, this module will fallback on a polyfill.
How to Create a context
the module has a single named function export called createContext
. You call it with 2 properties :
- an initial context object with the keys of the properties and their initial values
- a
stateValidator
predicate which can be used to validate a context change agains some values. it is invoked withstateValidator({ property, value })
where property is the name of the property that is being modified, and the new value.
The createContext
function will automatically create setters functions for all the properties you declare in the initial context. For instance, if your initial context is { foo: "someVal", barBaz: "someOtherVal" }
, the function will automatically create setFoo
and setBarBaz
functions to manipulate these values in the context.
The function returns an object with components & decorators to consume & provide the context. from the jsdoc :
/** * @typedef Context * @property * @property * @property * @property * to the wrapped component */ /** * Creates a React Context and returns consumer & providers components & decorators to interact * with this context. * @param * @param * @returns */
Usage
; const initialContext = appState: "Loading" ;const stateValidator = property === "appState" && "laoding" "in_background" "launched";// stateValidator is optional, but it's convenient to avoid bugs when consumers try to update either an incorrect property or an incorrect value const AppStateContext = ; const MyReactApp = <View> <AppStateContextConsumer> <View><Text>AppState is appState</Text><View> </AppStateContextConsumer> /* ... */ <AppStateContextConsumer> <Button label="App Finished Loading" onPress= /> </AppStateContextConsumer> </View> // don't forget the Provider !const DecoratedApp = AppStateContext; ;
Or with the classic Counter example
; const initialContext = counter: 0 ;const stateValidator = property === "counter" && typeof value === "number" && !Number; const CounterContext = ; // Using the decorator or the component with a render function is almost equivalent// Just beware of props override if using the decorator. If you have a prop with a name that is also in the context,// the value from the context will override the propconst Header = CounterContext; const CounterApp = { <View> <Header /> <CounterContextConsumer> <View> <Button label="increment" onPress= /> <Button label="decrement" onPress= /> <Button label="reset" onPress= /> </View> </CounterContextConsumer> </View>;}; // We could also have wrapped `CounterApp` with <CounterContext.Provider>const DecoratedCounterApp = CounterContext;
This pattern is very powerful as the different consumers (whether to get the value or to set it) can be in completely different locations in the code base. This avoids having to resort to Redux and it's consequent boilerplate code to easily pass values & their setters up & down the tree in a react app. It also helps to keep things in isolation properly
How to provide the context
the module offers 2 ways of providing the context
- with a component
; const MyComponent = <AppStateContextProvider> <View>/* anything... */</View> </AppStateContextProvider>; ;
- with a decorator
; const MyComponent = AppStateContext; ;
How to consume a context
Similarly, leveraging the context can be done in 2 ways :
- with the consumer component
; { return <View> <Text> Header </Text> <AppStateContextConsumer> { return <View> <Text>App is in appState</Text> <View> <Button label="set app to launched" onPress= /> </View> </View> ; } </AppStateContextConsumer> </View> ;}
- with the decorator
; { return <View> <Text> Header </Text> <View> <Text>App is in appState</Text> <View> <Button label="set app to launched" onPress= /> </View> </View> </View> ;} const MyDecoratedComponent = ;