Last active
January 17, 2019 17:49
-
-
Save dfoverdx/4afe0e4ad4bda6702b9debf63ca6cd9c to your computer and use it in GitHub Desktop.
React Context Factory for Next.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import App, { Container } from 'next/app'; | |
import React, { Component } from 'react'; | |
import { ExampleContextProvider, ExampleContextType } from './example-context'; | |
interface Props { | |
pageProps: {}; | |
Component: Component; | |
exampleContext?: ExampleContextType; | |
} | |
export default class MyApp extends App<Props> { | |
static async getInitialProps({ Component, ctx }) { | |
let pageProps: { [key: string]: any } = {}, | |
req = ctx.req; | |
if (Component.getInitialProps) { | |
Object.assign(pageProps, await Component.getInitialProps(ctx)); | |
} | |
if (!req) { | |
// client-side transition | |
return { pageProps }; | |
} | |
let exampleContext: ExampleContextType = { | |
value1: req.value1, | |
value2: req.value2 | |
}; | |
return { pageProps, exampleContext }; | |
} | |
render() { | |
const { Component, pageProps, exampleContext } = this.props; | |
return ( | |
<Container> | |
<ExampleContextProvider value={exampleContext}> | |
<Component {...pageProps} /> | |
</ExampleContextProvider> | |
</Container> | |
); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
/** | |
* A simple wrapper of a context's value. | |
*/ | |
interface Props<T> { | |
value?: T; | |
} | |
/** | |
* Function that generates a bitmap of changed values between two sets of properties. | |
* | |
* @see https://hph.is/coding/bitmasks-react-context | |
*/ | |
type CalculateChangedBits<T> = (prevValue?: T, nextValue?: T) => number; | |
type ContextFactoryResult<T> = { | |
/** | |
* The `React.Context` created. | |
*/ | |
Context: React.Context<T>; | |
/** | |
* The provider component that wraps the context. | |
*/ | |
ContextProvider: new(...args: any[]) => Component<Props<T>, T>; | |
} | |
/** | |
* Creates a client-side page-transition-resilient context. | |
* | |
* @param defaultValue Default value passed to `React.createContext()` | |
* @param displayName The `displayName` of the returned context | |
* @param calculateChangedBits Optional function that returns a bitmask representing which value properties were | |
* changed. | |
*/ | |
export default function createContext<T>( | |
defaultValue: T, | |
displayName: string, | |
calculateChangedBits?: CalculateChangedBits<T> | |
): ContextFactoryResult<T> { | |
const Context = React.createContext(defaultValue, calculateChangedBits); | |
Context.displayName = displayName; | |
class ContextProvider extends Component<Props<T>, T> { | |
constructor(props: Props<T>) { | |
super(props); | |
this.state = props && props.value || defaultValue; | |
} | |
/** | |
* When props are updated, we set the state to the new props value if it isn't undefined. When updating the | |
* state, we don't want to rerender, since the new rendering will be identical to the previous one. | |
*/ | |
private updatingState: boolean = false; | |
componentDidUpdate() { | |
if (this.props.value) { | |
this.updatingState = true; | |
this.setState(this.props.value); | |
} | |
} | |
shouldComponentUpdate() { | |
if (this.updatingState) { | |
this.updatingState = false; | |
return false; | |
} | |
return true; | |
} | |
render() { | |
return ( | |
<Context.Provider value={this.props.value || this.state}> | |
{this.props.children} | |
</Context.Provider> | |
); | |
} | |
} | |
return { | |
Context, | |
ContextProvider | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import createContext from './context-factory'; | |
export interface ExampleContextType { | |
value1: string, | |
value2: number | |
} | |
const defaultValue: ExampleContextType = { | |
value1: '', | |
value2: 0 | |
}; | |
const { | |
Context: ExampleContext, | |
ContextProvider | |
} = createContext<ExampleContextType>(defaultValue, 'ExampleContext'); | |
export default ExampleContext; | |
export const ExampleContextProvider = ContextProvider; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment