[private] Disallow useContext in class components

This commit is contained in:
Sophie Alpert
2018-09-26 12:48:33 -07:00
committed by Andrew Clark
parent 2888e06af6
commit 13cd5a2d20
5 changed files with 72 additions and 35 deletions

View File

@@ -9,26 +9,28 @@
import {readContext} from './ReactFiberNewContext';
import {
useState,
useReducer,
useEffect,
useMutationEffect,
useLayoutEffect,
useCallback,
useMemo,
useRef,
useAPI,
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useMutationEffect,
useReducer,
useRef,
useState,
} from './ReactFiberHooks';
export const Dispatcher = {
readContext,
useState,
useReducer,
useEffect,
useMutationEffect,
useLayoutEffect,
useCallback,
useMemo,
useRef,
useAPI,
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useMutationEffect,
useReducer,
useRef,
useState,
};

View File

@@ -7,11 +7,13 @@
* @flow
*/
import type {ReactContext} from 'shared/ReactTypes';
import type {Fiber} from './ReactFiber';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {HookEffectTag} from './ReactHookEffectTags';
import {NoWork} from './ReactFiberExpirationTime';
import {readContext} from './ReactFiberNewContext';
import {
Snapshot as SnapshotEffect,
Update as UpdateEffect,
@@ -318,6 +320,16 @@ function basicStateReducer<S>(state: S, action: BasicStateAction<S>): S {
return typeof action === 'function' ? action(state) : action;
}
export function useContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
// Ensure we're in a functional component (class components support only the
// .unstable_read() form)
resolveCurrentlyRenderingFiber();
return readContext(context, observedBits);
}
export function useState<S>(
initialState: S | (() => S),
): [S, Dispatch<S, BasicStateAction<S>>] {

View File

@@ -56,12 +56,12 @@ describe('ReactNewContext', () => {
},
);
sharedContextTests(
'useContext inside class component',
'unstable_read inside class component',
Context =>
class Consumer extends React.Component {
render() {
const observedBits = this.props.unstable_observedBits;
const contextValue = useContext(Context, observedBits);
const contextValue = Context.unstable_read(observedBits);
const render = this.props.children;
return render(contextValue);
}
@@ -1195,7 +1195,7 @@ describe('ReactNewContext', () => {
return (
<FooContext>
{foo => {
const bar = useContext(BarContext);
const bar = BarContext.unstable_read();
return <Text text={`Foo: ${foo}, Bar: ${bar}`} />;
}}
</FooContext>
@@ -1330,6 +1330,19 @@ describe('ReactNewContext', () => {
span('Baz: 2'),
]);
});
it('throws when used in a class component', () => {
const Context = React.createContext(0);
class Foo extends React.Component {
render() {
return useContext(Context);
}
}
ReactNoop.render(<Foo />);
expect(ReactNoop.flush).toThrow(
'Hooks can only be called inside the body of a functional component.',
);
});
});
it('unwinds after errors in complete phase', () => {

View File

@@ -24,20 +24,21 @@ import {
cloneElement,
isValidElement,
} from './ReactElement';
import {readContext, createContext} from './ReactContext';
import {createContext} from './ReactContext';
import {lazy} from './ReactLazy';
import forwardRef from './forwardRef';
import pure from './pure';
import {
useState,
useAPI,
useCallback,
useContext,
useEffect,
useLayoutEffect,
useMemo,
useMutationEffect,
useReducer,
useRef,
useEffect,
useMutationEffect,
useLayoutEffect,
useCallback,
useMemo,
useAPI,
useState,
} from './ReactHooks';
import {
createElementWithValidation,
@@ -84,16 +85,16 @@ if (enableSuspense) {
}
if (enableHooks_DEPRECATED) {
React.useContext = readContext;
React.useState = useState;
React.useAPI = useAPI;
React.useCallback = useCallback;
React.useContext = useContext;
React.useEffect = useEffect;
React.useLayoutEffect = useLayoutEffect;
React.useMemo = useMemo;
React.useMutationEffect = useMutationEffect;
React.useReducer = useReducer;
React.useRef = useRef;
React.useEffect = useEffect;
React.useMutationEffect = useMutationEffect;
React.useLayoutEffect = useLayoutEffect;
React.useCallback = useCallback;
React.useMemo = useMemo;
React.useAPI = useAPI;
React.useState = useState;
}
export default React;

View File

@@ -7,6 +7,7 @@
* @flow
*/
import type {ReactContext} from 'shared/ReactTypes';
import invariant from 'shared/invariant';
import ReactCurrentOwner from './ReactCurrentOwner';
@@ -20,6 +21,14 @@ function resolveDispatcher() {
return dispatcher;
}
export function useContext<T>(
context: ReactContext<T>,
observedBits: void | number | boolean,
): T {
const dispatcher = resolveDispatcher();
return dispatcher.useContext(context, observedBits);
}
export function useState<S>(initialState: S | (() => S)) {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);