/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import * as React from 'react';
import {createContext, Component, useContext, useState} from 'react';
import PropTypes from 'prop-types';
import type {ReactContext} from 'shared/ReactTypes';
function someNamedFunction() {}
function formatContextForDisplay(name: string, value: any | string) {
return (
<li>
{name}: <pre>{JSON.stringify(value, null, 2)}</pre>
</li>
);
}
const contextData = {
array: ['first', 'second', 'third'],
bool: true,
func: someNamedFunction,
number: 123,
object: {outer: {inner: {}}},
string: 'abc',
symbol: Symbol.for('symbol'),
null: null,
undefined: undefined,
};
class LegacyContextProvider extends Component<any> {
static childContextTypes: {
array: any,
bool: any,
func: any,
null: any,
number: any,
object: any,
string: any,
symbol: any,
undefined: any,
} = {
array: PropTypes.array,
bool: PropTypes.bool,
func: PropTypes.func,
number: PropTypes.number,
object: PropTypes.object,
string: PropTypes.string,
symbol: PropTypes.symbol,
null: PropTypes.any,
undefined: PropTypes.any,
};
getChildContext(): {
array: Array<string>,
bool: boolean,
func: () => void,
null: null,
number: number,
object: {outer: {inner: {...}}},
string: string,
symbol: symbol,
undefined: void,
} {
return contextData;
}
render(): any {
return this.props.children;
}
}
class LegacyContextConsumer extends Component<any> {
static contextTypes: {
array: any,
bool: any,
func: any,
null: any,
number: any,
object: any,
string: any,
symbol: any,
undefined: any,
} = {
array: PropTypes.array,
bool: PropTypes.bool,
func: PropTypes.func,
number: PropTypes.number,
object: PropTypes.object,
string: PropTypes.string,
symbol: PropTypes.symbol,
null: PropTypes.any,
undefined: PropTypes.any,
};
render(): any {
return formatContextForDisplay('LegacyContextConsumer', this.context);
}
}
class LegacyContextProviderWithUpdates extends Component<any> {
constructor(props: any) {
super(props);
this.state = {type: 'desktop'};
}
getChildContext(): {type: any} {
return {type: this.state.type};
}
// $FlowFixMe[missing-local-annot]
handleChange = event => {
this.setState({type: event.target.value});
};
render(): any {
return (
<>
<LegacyFunctionalContextConsumer />
<div>
<input value={this.state.type} onChange={this.handleChange} />
</div>
</>
);
}
}
LegacyContextProviderWithUpdates.childContextTypes = {
type: PropTypes.string,
};
// $FlowFixMe[missing-local-annot]
function LegacyFunctionalContextConsumer(props: any, context) {
return formatContextForDisplay('LegacyFunctionContextConsumer', context.type);
}
LegacyFunctionalContextConsumer.contextTypes = {
type: PropTypes.string,
};
const ModernContext = createContext();
ModernContext.displayName = 'ModernContext';
const ArrayContext = createContext(contextData.array);
ArrayContext.displayName = 'ArrayContext';
const BoolContext = createContext(contextData.bool);
BoolContext.displayName = 'BoolContext';
const FuncContext = createContext(contextData.func);
FuncContext.displayName = 'FuncContext';
const NumberContext = createContext(contextData.number);
NumberContext.displayName = 'NumberContext';
const StringContext = createContext(contextData.string);
StringContext.displayName = 'StringContext';
const SymbolContext = createContext(contextData.symbol);
SymbolContext.displayName = 'SymbolContext';
const NullContext = createContext(null);
NullContext.displayName = 'NullContext';
const UndefinedContext = createContext(undefined);
UndefinedContext.displayName = 'UndefinedContext';
class ModernContextType extends Component<any> {
static contextType: ReactContext<void> = ModernContext;
render(): any {
return formatContextForDisplay('ModernContextType', this.context);
}
}
function FunctionalContextConsumer() {
const value = useContext(StringContext);
return formatContextForDisplay('FunctionalContextConsumer', value);
}
const StringContextWithUpdates = createContext({
string: contextData.string,
setString: (string: string) => {},
});
const StringContextWithUpdates2 = createContext({
string2: contextData.string,
setString2: (string: string) => {},
});
function FunctionalContextProviderWithContextUpdates() {
const [string, setString] = useState(contextData.string);
const [string2, setString2] = useState(contextData.string);
const value = {string, setString};
const value2 = {string2, setString2};
return (
<StringContextWithUpdates.Provider value={value}>
<StringContextWithUpdates2.Provider value={value2}>
<FunctionalContextConsumerWithContextUpdates />
</StringContextWithUpdates2.Provider>
</StringContextWithUpdates.Provider>
);
}
function FunctionalContextConsumerWithContextUpdates() {
const {string, setString} = useContext(StringContextWithUpdates);
const {string2, setString2} = useContext(StringContextWithUpdates2);
const [state, setState] = useState('state');
// $FlowFixMe[missing-local-annot]
const handleChange = e => setString(e.target.value);
// $FlowFixMe[missing-local-annot]
const handleChange2 = e => setString2(e.target.value);
return (
<>
{formatContextForDisplay(
'FunctionalContextConsumerWithUpdates',
`context: ${string}, context 2: ${string2}`,
)}
<div>
context: <input value={string} onChange={handleChange} />
</div>
<div>
context 2: <input value={string2} onChange={handleChange2} />
</div>
<div>
{state}
<div>
test state:{' '}
<input value={state} onChange={e => setState(e.target.value)} />
</div>
</div>
</>
);
}
class ModernClassContextProviderWithUpdates extends Component<any> {
constructor(props: any) {
super(props);
this.setString = string => {
this.setState({string});
};
this.state = {
string: contextData.string,
setString: this.setString,
};
}
render(): any {
return (
<StringContextWithUpdates.Provider value={this.state}>
<ModernClassContextConsumerWithUpdates />
</StringContextWithUpdates.Provider>
);
}
}
class ModernClassContextConsumerWithUpdates extends Component<any> {
render(): any {
return (
<StringContextWithUpdates.Consumer>
{({string, setString}: {string: string, setString: string => void}) => (
<>
{formatContextForDisplay(
'ModernClassContextConsumerWithUpdates',
string,
)}
<input value={string} onChange={e => setString(e.target.value)} />
</>
)}
</StringContextWithUpdates.Consumer>
);
}
}
export default function Contexts(): React.Node {
return (
<div>
<h1>Contexts</h1>
<ul>
<LegacyContextProvider>
<LegacyContextConsumer />
</LegacyContextProvider>
<LegacyContextProviderWithUpdates />
<ModernContext.Provider value={contextData}>
<ModernContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('ModernContext.Consumer', value)
}
</ModernContext.Consumer>
<ModernContextType />
</ModernContext.Provider>
<FunctionalContextConsumer />
<FunctionalContextProviderWithContextUpdates />
<ModernClassContextProviderWithUpdates />
<ArrayContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('ArrayContext.Consumer', value)
}
</ArrayContext.Consumer>
<BoolContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('BoolContext.Consumer', value)
}
</BoolContext.Consumer>
<FuncContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('FuncContext.Consumer', value)
}
</FuncContext.Consumer>
<NumberContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('NumberContext.Consumer', value)
}
</NumberContext.Consumer>
<StringContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('StringContext.Consumer', value)
}
</StringContext.Consumer>
<SymbolContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('SymbolContext.Consumer', value)
}
</SymbolContext.Consumer>
<NullContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('NullContext.Consumer', value)
}
</NullContext.Consumer>
<UndefinedContext.Consumer>
{(value: $FlowFixMe) =>
formatContextForDisplay('UndefinedContext.Consumer', value)
}
</UndefinedContext.Consumer>
</ul>
</div>
);
}