[react] Breaking! Update hooks API to (what's expected to be) the final 16.8.0 API (#32659)

* Update hooks API to (what's expected to be) the final 16.8.0 API

* Remove unneeded compatibility alias
This commit is contained in:
Jessica Franco
2019-02-02 07:14:09 +09:00
committed by John Reilly
parent 59d0d56653
commit c8477f17f5
2 changed files with 100 additions and 21 deletions

View File

@@ -775,9 +775,13 @@ declare namespace React {
type Dispatch<A> = (value: A) => void;
// Unlike redux, the actions _can_ be anything
type Reducer<S, A> = (prevState: S, action: A) => S;
// types used to try and prevent the compiler from reducing S
// to a supertype common with the second argument to useReducer()
type ReducerState<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never;
type ReducerAction<R extends Reducer<any, any>> = R extends Reducer<any, infer A> ? A : never;
// The identity check is done with the SameValue algorithm (Object.is), which is stricter than ===
// TODO (TypeScript 3.0): ReadonlyArray<unknown>
type InputIdentityList = ReadonlyArray<any>;
type DependencyList = ReadonlyArray<any>;
// NOTE: Currently, in alpha.0, the effect callbacks are actually allowed to return anything,
// but functions are treated specially. The next version published with hooks will warn if you actually
@@ -794,14 +798,14 @@ declare namespace React {
* Accepts a context object (the value returned from `React.createContext`) and returns the current
* context value, as given by the nearest context provider for the given context.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usecontext
*/
function useContext<T>(context: Context<T>/*, (not public API) observedBits?: number|boolean */): T;
/**
* Returns a stateful value, and a function to update it.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usestate
*/
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
@@ -812,10 +816,54 @@ declare namespace React {
* multiple sub-values. It also lets you optimize performance for components that trigger deep
* updates because you can pass `dispatch` down instead of callbacks.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
*/
function useReducer<S, A>(reducer: Reducer<S, A>, initialState: S, initialAction?: A | null): [S, Dispatch<A>];
// overload where "I" may be a subset of ReducerState<R>; used to provide autocompletion.
// If "I" matches ReducerState<R> exactly then the last overload will allow initializer to be ommitted.
// the last overload effectively behaves as if the identity function (x => x) is the initializer.
function useReducer<R extends Reducer<any, any>, I>(
reducer: R,
initializerArg: I & ReducerState<R>,
initializer: (arg: I & ReducerState<R>) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
/**
* An alternative to `useState`.
*
* `useReducer` is usually preferable to `useState` when you have complex state logic that involves
* multiple sub-values. It also lets you optimize performance for components that trigger deep
* updates because you can pass `dispatch` down instead of callbacks.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
*/
// overload for free "I"; all goes as long as initializer converts it into "ReducerState<R>".
function useReducer<R extends Reducer<any, any>, I>(
reducer: R,
initializerArg: I,
initializer: (arg: I) => ReducerState<R>
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
/**
* An alternative to `useState`.
*
* `useReducer` is usually preferable to `useState` when you have complex state logic that involves
* multiple sub-values. It also lets you optimize performance for components that trigger deep
* updates because you can pass `dispatch` down instead of callbacks.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usereducer
*/
// I'm not sure if I keep this 2-ary or if I make it (2,3)-ary; it's currently (2,3)-ary.
// The Flow types do have an overload for 3-ary invocation with undefined initializer.
// NOTE: the documentation or any alphas aren't updated, this is current for master.
// NOTE 2: without the ReducerState indirection, TypeScript would reduce S to be the most common
// supertype between the reducer's return type and the initialState (or the initializer's return type),
// which would prevent autocompletion from ever working.
function useReducer<R extends Reducer<any, any>>(
reducer: R,
initialState: ReducerState<R>,
initializer?: undefined
): [ReducerState<R>, Dispatch<ReducerAction<R>>];
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
@@ -823,7 +871,7 @@ declare namespace React {
* Note that `useRef()` is useful for more than the `ref` attribute. Its handy for keeping any mutable
* value around similar to how youd use instance fields in classes.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
// TODO (TypeScript 3.0): <T extends unknown>
@@ -839,7 +887,7 @@ declare namespace React {
* Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
* of the generic argument.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
// TODO (TypeScript 3.0): <T extends unknown>
@@ -854,20 +902,20 @@ declare namespace React {
* If youre migrating code from a class component, `useLayoutEffect` fires in the same phase as
* `componentDidMount` and `componentDidUpdate`.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#uselayouteffect
*/
function useLayoutEffect(effect: EffectCallback, inputs?: InputIdentityList): void;
function useLayoutEffect(effect: EffectCallback, deps?: DependencyList): void;
/**
* Accepts a function that contains imperative, possibly effectful code.
*
* @param effect Imperative function that can return a cleanup function
* @param inputs If present, effect will only activate if the values in the list change.
* @param deps If present, effect will only activate if the values in the list change.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useeffect
*/
function useEffect(effect: EffectCallback, inputs?: InputIdentityList): void;
function useEffect(effect: EffectCallback, deps?: DependencyList): void;
// NOTE: this does not accept strings, but this will have to be fixed by removing strings from type Ref<T>
/**
* `useImperativeHandle` customizes the instance value that is exposed to parent components when using
@@ -875,23 +923,23 @@ declare namespace React {
*
* `useImperativeHandle` should be used with `React.forwardRef`.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useimperativehandle
*/
function useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, inputs?: InputIdentityList): void;
function useImperativeHandle<T, R extends T>(ref: Ref<T>|undefined, init: () => R, deps?: DependencyList): void;
// I made 'inputs' required here and in useMemo as there's no point to memoizing without the memoization key
// useCallback(X) is identical to just using X, useMemo(() => Y) is identical to just using Y.
/**
* `useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
* has changed.
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usecallback
*/
// TODO (TypeScript 3.0): <T extends (...args: never[]) => unknown>
function useCallback<T extends (...args: any[]) => any>(callback: T, inputs: InputIdentityList): T;
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;
/**
* `useMemo` will only recompute the memoized value when one of the `inputs` has changed.
* `useMemo` will only recompute the memoized value when one of the `deps` has changed.
*
* Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in
* the second argument.
@@ -905,10 +953,22 @@ declare namespace React {
* }
* ```
*
* @version experimental
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usememo
*/
function useMemo<T>(factory: () => T, inputs: InputIdentityList): T;
function useMemo<T>(factory: () => T, deps: DependencyList): T;
/**
* `useDebugValue` can be used to display a label for custom hooks in React DevTools.
*
* NOTE: We dont recommend adding debug values to every custom hook.
* Its most valuable for custom hooks that are part of shared libraries.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usedebugvalue
*/
// the name of the custom hook is itself derived from the function name at runtime:
// it's just the function name without the "use" prefix.
function useDebugValue<T>(value: T, format?: (value: T) => any): void;
//
// Event System

View File

@@ -63,7 +63,13 @@ export function App() {
const birthdayRef = React.useRef<FancyButton>(null);
React.useLayoutEffect(() => {
birthdayRef.current!.fancyClick();
if (birthdayRef.current !== null) {
birthdayRef.current.fancyClick();
} else {
// this looks redundant but it ensures the type actually has "null" in it instead of "never"
// $ExpectType null
birthdayRef.current;
}
});
return <>
@@ -85,13 +91,22 @@ const context = React.createContext<Context>({ test: true });
function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean {
const value: Context = React.useContext(context);
const [, setState] = React.useState(() => 0);
const [reducerState, dispatch] = React.useReducer(reducer, initialState, { type: 'resetAge' });
// Bonus typescript@next version
// const [reducerState, dispatch] = React.useReducer(reducer, true as const, arg => arg && initialState);
// Compile error in typescript@3.0 but not in typescript@3.1.
// const [reducerState, dispatch] = React.useReducer(reducer, true as true, arg => arg && initialState);
const [reducerState, dispatch] = React.useReducer(reducer, true as true, (arg: true): AppState => arg && initialState);
// inline object, to (manually) check if autocomplete works
React.useReducer(reducer, { age: 42, name: 'The Answer' });
// make sure this is not going to the |null overload
// $ExpectType MutableRefObject<boolean>
const didLayout = React.useRef(false);
const id = React.useMemo(() => Math.random(), []);
React.useImperativeHandle(ref, () => ({ id }), [id]);
// was named like this in the first alpha, renamed before release
// $ExpectError
React.useImperativeMethods(ref, () => ({}), [id]);
@@ -105,6 +120,9 @@ function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean {
setState(reducerState.age);
}, []);
React.useDebugValue(id, value => value.toFixed());
React.useDebugValue(id);
return React.useCallback(() => didLayout.current, []);
}
@@ -118,5 +136,6 @@ const UsesEveryHook = React.forwardRef(
);
const everyHookRef = React.createRef<{ id: number }>();
<UsesEveryHook ref={everyHookRef}/>;
// TODO: "implicit any" in typescript@3.0 but not in typescript@3.1
// <UsesEveryHook ref={ref => { ref && console.log(ref.id); }}/>;