Skip to content

Commit 51c0791

Browse files
authored
Warn when second argument is passed to useCallback (#14729)
1 parent 70d4075 commit 51c0791

File tree

3 files changed

+246
-124
lines changed

3 files changed

+246
-124
lines changed

Diff for: packages/react-dom/src/__tests__/ReactDOMServerIntegrationHooks-test.internal.js

+110-41
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,114 @@ describe('ReactDOMServerHooks', () => {
613613
});
614614

615615
describe('useContext', () => {
616+
itThrowsWhenRendering(
617+
'if used inside a class component',
618+
async render => {
619+
const Context = React.createContext({}, () => {});
620+
class Counter extends React.Component {
621+
render() {
622+
let [count] = useContext(Context);
623+
return <Text text={count} />;
624+
}
625+
}
626+
627+
return render(<Counter />);
628+
},
629+
'Hooks can only be called inside the body of a function component.',
630+
);
631+
});
632+
633+
itRenders(
634+
'can use the same context multiple times in the same function',
635+
async render => {
636+
const Context = React.createContext({foo: 0, bar: 0, baz: 0});
637+
638+
function Provider(props) {
639+
return (
640+
<Context.Provider
641+
value={{foo: props.foo, bar: props.bar, baz: props.baz}}>
642+
{props.children}
643+
</Context.Provider>
644+
);
645+
}
646+
647+
function FooAndBar() {
648+
const {foo} = useContext(Context);
649+
const {bar} = useContext(Context);
650+
return <Text text={`Foo: ${foo}, Bar: ${bar}`} />;
651+
}
652+
653+
function Baz() {
654+
const {baz} = useContext(Context);
655+
return <Text text={'Baz: ' + baz} />;
656+
}
657+
658+
class Indirection extends React.Component {
659+
render() {
660+
return this.props.children;
661+
}
662+
}
663+
664+
function App(props) {
665+
return (
666+
<div>
667+
<Provider foo={props.foo} bar={props.bar} baz={props.baz}>
668+
<Indirection>
669+
<Indirection>
670+
<FooAndBar />
671+
</Indirection>
672+
<Indirection>
673+
<Baz />
674+
</Indirection>
675+
</Indirection>
676+
</Provider>
677+
</div>
678+
);
679+
}
680+
681+
const domNode = await render(<App foo={1} bar={3} baz={5} />);
682+
expect(clearYields()).toEqual(['Foo: 1, Bar: 3', 'Baz: 5']);
683+
expect(domNode.childNodes.length).toBe(2);
684+
expect(domNode.firstChild.tagName).toEqual('SPAN');
685+
expect(domNode.firstChild.textContent).toEqual('Foo: 1, Bar: 3');
686+
expect(domNode.lastChild.tagName).toEqual('SPAN');
687+
expect(domNode.lastChild.textContent).toEqual('Baz: 5');
688+
},
689+
);
690+
691+
itRenders('warns when bitmask is passed to useContext', async render => {
692+
let Context = React.createContext('Hi');
693+
694+
function Foo() {
695+
return <span>{useContext(Context, 1)}</span>;
696+
}
697+
698+
const domNode = await render(<Foo />, 1);
699+
expect(domNode.textContent).toBe('Hi');
700+
});
701+
702+
describe('useDebugValue', () => {
703+
itRenders('is a noop', async render => {
704+
function Counter(props) {
705+
const debugValue = useDebugValue(123);
706+
return <Text text={typeof debugValue} />;
707+
}
708+
709+
const domNode = await render(<Counter />);
710+
expect(domNode.textContent).toEqual('undefined');
711+
});
712+
});
713+
714+
describe('readContext', () => {
715+
function readContext(Context, observedBits) {
716+
const dispatcher =
717+
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
718+
.ReactCurrentDispatcher.current;
719+
return dispatcher.readContext(Context, observedBits);
720+
}
721+
616722
itRenders(
617-
'can use the same context multiple times in the same function',
723+
'can read the same context multiple times in the same function',
618724
async render => {
619725
const Context = React.createContext(
620726
{foo: 0, bar: 0, baz: 0},
@@ -643,13 +749,13 @@ describe('ReactDOMServerHooks', () => {
643749
}
644750

645751
function FooAndBar() {
646-
const {foo} = useContext(Context, 0b001);
647-
const {bar} = useContext(Context, 0b010);
752+
const {foo} = readContext(Context, 0b001);
753+
const {bar} = readContext(Context, 0b010);
648754
return <Text text={`Foo: ${foo}, Bar: ${bar}`} />;
649755
}
650756

651757
function Baz() {
652-
const {baz} = useContext(Context, 0b100);
758+
const {baz} = readContext(Context, 0b100);
653759
return <Text text={'Baz: ' + baz} />;
654760
}
655761

@@ -689,43 +795,6 @@ describe('ReactDOMServerHooks', () => {
689795
},
690796
);
691797

692-
itThrowsWhenRendering(
693-
'if used inside a class component',
694-
async render => {
695-
const Context = React.createContext({}, () => {});
696-
class Counter extends React.Component {
697-
render() {
698-
let [count] = useContext(Context);
699-
return <Text text={count} />;
700-
}
701-
}
702-
703-
return render(<Counter />);
704-
},
705-
'Hooks can only be called inside the body of a function component.',
706-
);
707-
});
708-
709-
describe('useDebugValue', () => {
710-
itRenders('is a noop', async render => {
711-
function Counter(props) {
712-
const debugValue = useDebugValue(123);
713-
return <Text text={typeof debugValue} />;
714-
}
715-
716-
const domNode = await render(<Counter />);
717-
expect(domNode.textContent).toEqual('undefined');
718-
});
719-
});
720-
721-
describe('readContext', () => {
722-
function readContext(Context, observedBits) {
723-
const dispatcher =
724-
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
725-
.ReactCurrentDispatcher.current;
726-
return dispatcher.readContext(Context, observedBits);
727-
}
728-
729798
itRenders('with a warning inside useMemo and useReducer', async render => {
730799
const Context = React.createContext(42);
731800

0 commit comments

Comments
 (0)