-
Notifications
You must be signed in to change notification settings - Fork 720
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hooks #32
Closed
Closed
Hooks #32
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
40cacc6
fix error in codeacademy svg
alexkrolick 4b5b678
lighten secondary color to make links stand out
alexkrolick 0869823
Add testHook docs
alexkrolick 01dd2d7
Add a blog post about hooks
alexkrolick 3574018
Merge branch 'master' into hooks
alexkrolick c56fffe
Merge branch 'master' into hooks
alexkrolick File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
--- | ||
id: example-react-hooks | ||
title: React Hooks | ||
--- | ||
|
||
`react-testing-library` provides the | ||
[`testHook`](/docs/react-testing-library/api#testhook) utility to test custom | ||
hooks. | ||
|
||
> **Note** | ||
> | ||
> This is the recommended way to test reusable custom react hooks. It is not | ||
> however recommended to use the testHook utility to test single-use custom | ||
> hooks. Typically those are better tested by testing the component that is | ||
> using it. | ||
|
||
## Using `result` | ||
|
||
Testing the last returned value of a hook using the `result` ref | ||
|
||
```jsx | ||
function useCounter({ initialCount = 0, step = 1 } = {}) { | ||
const [count, setCount] = React.useState(initialCount) | ||
const increment = () => setCount(c => c + step) | ||
const decrement = () => setCount(c => c - step) | ||
return { count, increment, decrement } | ||
} | ||
``` | ||
|
||
```jsx | ||
test('returns result ref with latest result from hook execution', () => { | ||
const { result } = testHook(useCounter) | ||
expect(result.current.count).toBe(0) | ||
act(() => { | ||
result.current.increment() | ||
}) | ||
expect(result.current.count).toBe(1) | ||
}) | ||
``` | ||
|
||
## State | ||
|
||
Testing a hook that provides state | ||
|
||
```jsx | ||
import { useState } from 'react' | ||
|
||
export function useCounter({ initialCount = 0, step = 1 } = {}) { | ||
const [count, setCount] = useState(initialCount) | ||
const increment = () => setCount(c => c + step) | ||
const decrement = () => setCount(c => c - step) | ||
return { count, increment, decrement } | ||
} | ||
``` | ||
|
||
```jsx | ||
import { testHook, act, cleanup } from 'react-testing-library' | ||
afterEach(cleanup) | ||
|
||
describe('useCounter', () => { | ||
test('accepts default initial values', () => { | ||
let count | ||
testHook(() => ({ count } = useCounter())) | ||
|
||
expect(count).toBe(0) | ||
}) | ||
|
||
test('accepts a default initial value for `count`', () => { | ||
let count | ||
testHook(() => ({ count } = useCounter({}))) | ||
|
||
expect(count).toBe(0) | ||
}) | ||
|
||
test('provides an `increment` function', () => { | ||
let count, increment | ||
testHook(() => ({ count, increment } = useCounter({ step: 2 }))) | ||
|
||
expect(count).toBe(0) | ||
act(() => { | ||
increment() | ||
}) | ||
expect(count).toBe(2) | ||
}) | ||
|
||
test('provides an `decrement` function', () => { | ||
let count, decrement | ||
testHook(() => ({ count, decrement } = useCounter({ step: 2 }))) | ||
|
||
expect(count).toBe(0) | ||
act(() => { | ||
decrement() | ||
}) | ||
expect(count).toBe(-2) | ||
}) | ||
|
||
test('accepts a default initial value for `step`', () => { | ||
let count, increment | ||
testHook(() => ({ count, increment } = useCounter({}))) | ||
|
||
expect(count).toBe(0) | ||
act(() => { | ||
increment() | ||
}) | ||
expect(count).toBe(1) | ||
}) | ||
}) | ||
``` | ||
|
||
## Unmount Side-Effects | ||
|
||
Using the `unmount` function to check useEffect behavior when unmounting | ||
|
||
```jsx | ||
import { useState, useEffect } from 'react' | ||
|
||
export function useDocumentTitle(title) { | ||
const [originalTitle, setOriginalTitle] = useState(document.title) | ||
useEffect(() => { | ||
setOriginalTitle(document.title) | ||
document.title = title | ||
return () => { | ||
document.title = originalTitle | ||
} | ||
}, [title]) | ||
} | ||
``` | ||
|
||
```jsx | ||
describe('useDocumentTitle', () => { | ||
test('sets a title', () => { | ||
document.title = 'original title' | ||
testHook(() => { | ||
useDocumentTitle('modified title') | ||
}) | ||
|
||
expect(document.title).toBe('modified title') | ||
}) | ||
|
||
test('returns to original title when component is unmounted', () => { | ||
document.title = 'original title' | ||
const { unmount } = testHook(() => { | ||
useDocumentTitle('modified title') | ||
}) | ||
|
||
unmount() | ||
expect(document.title).toBe('original title') | ||
}) | ||
}) | ||
``` | ||
|
||
## Rerender Side-Effects | ||
|
||
Using the `rerender` function to test calling useEffect multiple times | ||
|
||
```jsx | ||
import { useEffect } from 'react' | ||
|
||
export function useCall(callback, deps) { | ||
useEffect(() => { | ||
callback() | ||
}, deps) | ||
} | ||
``` | ||
|
||
```jsx | ||
describe('useCall', () => { | ||
test('calls once on render', () => { | ||
const spy = jest.fn() | ||
testHook(() => { | ||
useCall(spy, []) | ||
}) | ||
expect(spy).toHaveBeenCalledTimes(1) | ||
}) | ||
|
||
test('calls again if deps change', () => { | ||
let deps = [false] | ||
const spy = jest.fn() | ||
const { rerender } = testHook(() => { | ||
useCall(spy, deps) | ||
}) | ||
expect(spy).toHaveBeenCalledTimes(1) | ||
|
||
deps = [true] | ||
rerender() | ||
expect(spy).toHaveBeenCalledTimes(2) | ||
}) | ||
|
||
test('does not call again if deps are the same', () => { | ||
let deps = [false] | ||
const spy = jest.fn() | ||
const { rerender } = testHook(() => { | ||
useCall(spy, deps) | ||
}) | ||
expect(spy).toHaveBeenCalledTimes(1) | ||
|
||
deps = [false] | ||
rerender() | ||
expect(spy).toHaveBeenCalledTimes(1) | ||
}) | ||
}) | ||
``` |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--- | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a little FYI blog post about hooks, since they are new in the docs |
||
title: React Hooks Are Supported | ||
author: Alex Krolick | ||
authorURL: http://github.com/alexkrolick | ||
--- | ||
|
||
[Hooks have been released in React 16.8](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks) | ||
and they are supported out of the box by `react-testing-library`! | ||
|
||
Because `react-testing-library` only uses the external interface of your React | ||
components, hooks work right away! If you rewrite a class component with hooks | ||
your tests should still pass. | ||
|
||
For unit testing custom hooks, we've also added a `testHook` utility. Check out | ||
the [docs for `testHook`](/docs/react-testing-library/api#testhook). Thanks to | ||
[@donavon](https://github.com/donavon) for the PR. |
This file contains 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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pulled in these examples from the rtl repo