This is one stop global knowledge base where you can learn about all the products, solutions and support features.
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
This page answers some of the frequently asked questions about Hooks.
Adoption Strategy
From Classes to Hooks
Performance Optimizations
Under the Hood
Starting with 16.8.0, React includes a stable implementation of React Hooks for:
Note that to enable Hooks, all React packages need to be 16.8.0 or higher . Hooks won’t work if you forget to update, for example, React DOM.
React Native 0.59 and above support Hooks.
No. There are no plans to remove classes from React — we all need to keep shipping products and can’t afford rewrites. We recommend trying Hooks in new code.
Hooks offer a powerful and expressive new way to reuse functionality between components. “Building Your Own Hooks” provides a glimpse of what’s possible. This article by a React core team member dives deeper into the new capabilities unlocked by Hooks.
Hooks are a more direct way to use the React features you already know — such as state, lifecycle, context, and refs. They don’t fundamentally change how React works, and your knowledge of components, props, and top-down data flow is just as relevant.
Hooks do have a learning curve of their own. If there’s something missing in this documentation, raise an issue and we’ll try to help.
When you’re ready, we’d encourage you to start trying Hooks in new components you write. Make sure everyone on your team is on board with using them and familiar with this documentation. We don’t recommend rewriting your existing classes to Hooks unless you planned to rewrite them anyway (e.g. to fix bugs).
You can’t use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
Our goal is for Hooks to cover all use cases for classes as soon as possible. There are no Hook equivalents to the uncommon
getSnapshotBeforeUpdate
,
getDerivedStateFromError
and
componentDidCatch
lifecycles yet, but we plan to add them soon.
Often, render props and higher-order components render only a single child. We think Hooks are a simpler way to serve this use case. There is still a place for both patterns (for example, a virtual scroller component might have a
renderItem
prop, or a visual container component might have its own DOM structure). But in most cases, Hooks will be sufficient and can help reduce nesting in your tree.
connect()
and React Router?
You can continue to use the exact same APIs as you always have; they’ll continue to work.
React Redux since v7.1.0 supports Hooks API and exposes hooks like
useDispatch
or
useSelector
.
React Router supports hooks since v5.1.
Other libraries might support hooks in the future too.
Hooks were designed with static typing in mind. Because they’re functions, they are easier to type correctly than patterns like higher-order components. The latest Flow and TypeScript React definitions include support for React Hooks.
Importantly, custom Hooks give you the power to constrain React API if you’d like to type them more strictly in some way. React gives you the primitives, but you can combine them in different ways than what we provide out of the box.
From React’s point of view, a component using Hooks is just a regular component. If your testing solution doesn’t rely on React internals, testing components with Hooks shouldn’t be different from how you normally test components.
Note
Testing Recipes include many examples that you can copy and paste.
For example, let’s say we have this counter component:
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
We’ll test it using React DOM. To make sure that the behavior matches what happens in the browser, we’ll wrap the code rendering and updating it into
ReactTestUtils.act()
calls:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { act } from 'react-dom/test-utils';import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and effect
act(() => { ReactDOM.createRoot(container).render(<Counter />); }); const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
// Test second render and effect
act(() => { button.dispatchEvent(new MouseEvent('click', {bubbles: true})); }); expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});
The calls to
act()
will also flush the effects inside of them.
If you need to test a custom Hook, you can do so by creating a component in your test, and using your Hook from it. Then you can test the component you wrote.
To reduce the boilerplate, we recommend using React Testing Library which is designed to encourage writing tests that use your components as the end users do.
For more information, check out Testing Recipes.
We provide an ESLint plugin that enforces rules of Hooks to avoid bugs. It assumes that any function starting with ”
use
” and a capital letter right after it is a Hook. We recognize this heuristic isn’t perfect and there may be some false positives, but without an ecosystem-wide convention there is just no way to make Hooks work well — and longer names will discourage people from either adopting Hooks or following the convention.
In particular, the rule enforces that:
PascalCase
function (assumed to be a component) or another
useSomething
function (assumed to be a custom Hook).
There are a few more heuristics, and they might change over time as we fine-tune the rule to balance finding bugs with avoiding false positives.
constructor
: Function components don’t need a constructor. You can initialize the state in the
useState
call. If computing the initial state is expensive, you can pass a function to
useState
.
getDerivedStateFromProps
: Schedule an update while rendering instead.
shouldComponentUpdate
: See
React.memo
below.
render
: This is the function component body itself.
componentDidMount
,
componentDidUpdate
,
componentWillUnmount
: The
useEffect
Hook can express all combinations of these (including less common cases).
getSnapshotBeforeUpdate
,
componentDidCatch
and
getDerivedStateFromError
: There are no Hook equivalents for these methods yet, but they will be added soon.
Here is a small demo to get you started. To learn more, check out this article about data fetching with Hooks.
Yes! The
useRef()
Hook isn’t just for DOM refs. The “ref” object is a generic container whose
current
property is mutable and can hold any value, similar to an instance property on a class.
You can write to it from inside
useEffect
:
function Timer() {
const intervalRef = useRef();
useEffect(() => {
const id = setInterval(() => {
// ...
});
intervalRef.current = id; return () => {
clearInterval(intervalRef.current);
};
});
// ...
}
If we just wanted to set an interval, we wouldn’t need the ref (
id
could be local to the effect), but it’s useful if we want to clear the interval from an event handler:
// ...
function handleCancelClick() {
clearInterval(intervalRef.current); }
// ...
Conceptually, you can think of refs as similar to instance variables in a class. Unless you’re doing lazy initialization, avoid setting refs during rendering — this can lead to surprising behavior. Instead, typically you want to modify refs in event handlers and effects.
If you’re coming from classes, you might be tempted to always call
useState()
once and put all state into a single object. You can do it if you’d like. Here is an example of a component that follows the mouse movement. We keep its position and size in the local state:
function Box() {
const [state, setState] = useState({ left: 0, top: 0, width: 100, height: 100 });
// ...
}
Now let’s say we want to write some logic that changes
left
and
top
when the user moves their mouse. Note how we have to merge these fields into the previous state object manually:
// ...
useEffect(() => {
function handleWindowMouseMove(e) {
// Spreading "...state" ensures we don't "lose" width and height setState(state => ({ ...state, left: e.pageX, top: e.pageY })); }
// Note: this implementation is a bit simplified
window.addEventListener('mousemove', handleWindowMouseMove);
return () => window.removeEventListener('mousemove', handleWindowMouseMove);
}, []);
// ...
This is because when we update a state variable, we
replace
its value. This is different from
this.setState
in a class, which
merges
the updated fields into the object.
If you miss automatic merging, you could write a custom
useLegacyState
Hook that merges object state updates. However,
we recommend to split state into multiple state variables based on which values tend to change together.
For example, we could split our component state into
position
and
size
objects, and always replace the
position
with no need for merging:
function Box() {
const [position, setPosition] = useState({ left: 0, top: 0 }); const [size, setSize] = useState({ width: 100, height: 100 });
useEffect(() => {
function handleWindowMouseMove(e) {
setPosition({ left: e.pageX, top: e.pageY }); }
// ...
Separating independent state variables also has another benefit. It makes it easy to later extract some related logic into a custom Hook, for example:
function Box() {
const position = useWindowPosition(); const [size, setSize] = useState({ width: 100, height: 100 });
// ...
}
function useWindowPosition() { const [position, setPosition] = useState({ left: 0, top: 0 });
useEffect(() => {
// ...
}, []);
return position;
}
Note how we were able to move the
useState
call for the
position
state variable and the related effect into a custom Hook without changing their code. If all state was in a single object, extracting it would be more difficult.
Both putting all state in a single
useState
call, and having a
useState
call per each field can work. Components tend to be most readable when you find a balance between these two extremes, and group related state into a few independent state variables. If the state logic becomes complex, we recommend managing it with a reducer or a custom Hook.
This is a rare use case. If you need it, you can use a mutable ref to manually store a boolean value corresponding to whether you are on the first or a subsequent render, then check that flag in your effect. (If you find yourself doing this often, you could create a custom Hook for it.)
There are two cases in which you might want to get previous props or state.
Sometimes, you need previous props to
clean up an effect.
For example, you might have an effect that subscribes to a socket based on the
userId
prop. If the
userId
prop changes, you want to unsubscribe from the
previous
userId
and subscribe to the
next
one. You don’t need to do anything special for this to work:
useEffect(() => {
ChatAPI.subscribeToSocket(props.userId);
return () => ChatAPI.unsubscribeFromSocket(props.userId);
}, [props.userId]);
In the above example, if
userId
changes from
3
to
4
,
ChatAPI.unsubscribeFromSocket(3)
will run first, and then
ChatAPI.subscribeToSocket(4)
will run. There is no need to get “previous”
userId
because the cleanup function will capture it in a closure.
Other times, you might need to adjust state based on a change in props or other state . This is rarely needed and is usually a sign you have some duplicate or redundant state. However, in the rare case that you need this pattern, you can store previous state or props in state and update them during rendering.
We have previously suggested a custom Hook called
usePrevious
to hold the previous value. However, we’ve found that most use cases fall into the two patterns described above. If your use case is different, you can hold a value in a ref and manually update it when needed. Avoid reading and updating refs during rendering because this makes your component’s behavior difficult to predict and understand.
Any function inside a component, including event handlers and effects, “sees” the props and state from the render it was created in. For example, consider code like this:
function Example() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
If you first click “Show alert” and then increment the counter, the alert will show the
count
variable
at the time you clicked the “Show alert” button
. This prevents bugs caused by the code assuming props and state don’t change.
If you intentionally want to read the latest state from some asynchronous callback, you could keep it in a ref, mutate it, and read from it.
Finally, another possible reason you’re seeing stale props or state is if you use the “dependency array” optimization but didn’t correctly specify all the dependencies. For example, if an effect specifies
[]
as the second argument but reads
someProp
inside, it will keep “seeing” the initial value of
someProp
. The solution is to either remove the dependency array, or to fix it. Here’s how you can deal with functions, and here’s other common strategies to run effects less often without incorrectly skipping dependencies.
Note
We provide an
exhaustive-deps
ESLint rule as a part of theeslint-plugin-react-hooks
package. It warns when dependencies are specified incorrectly and suggests a fix.
getDerivedStateFromProps
?
While you probably don’t need it, in rare cases that you do (such as implementing a
<Transition>
component), you can update the state right during rendering. React will re-run the component with updated state immediately after exiting the first render so it wouldn’t be expensive.
Here, we store the previous value of the
row
prop in a state variable so that we can compare:
function ScrollView({row}) {
const [isScrollingDown, setIsScrollingDown] = useState(false);
const [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
setIsScrollingDown(prevRow !== null && row > prevRow);
setPrevRow(row);
}
return `Scrolling down: ${isScrollingDown}`;
}
This might look strange at first, but an update during rendering is exactly what
getDerivedStateFromProps
has always been like conceptually.
Both
useState
and
useReducer
Hooks bail out of updates if the next value is the same as the previous one. Mutating state in place and calling
setState
will not cause a re-render.
Normally, you shouldn’t mutate local state in React. However, as an escape hatch, you can use an incrementing counter to force a re-render even if the state has not changed:
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
Try to avoid this pattern if possible.
While you shouldn’t need this often, you may expose some imperative methods to a parent component with the
useImperativeHandle
Hook.
One rudimentary way to measure the position or size of a DOM node is to use a callback ref. React will call that callback whenever the ref gets attached to a different node. Here is a small demo:
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => { if (node !== null) { setHeight(node.getBoundingClientRect().height); } }, []);
return (
<>
<h1 ref={measuredRef}>Hello, world</h1> <h2>The above header is {Math.round(height)}px tall</h2>
</>
);
}
We didn’t choose
useRef
in this example because an object ref doesn’t notify us about
changes
to the current ref value. Using a callback ref ensures that even if a child component displays the measured node later (e.g. in response to a click), we still get notified about it in the parent component and can update the measurements.
Note that we pass
[]
as a dependency array to
useCallback
. This ensures that our ref callback doesn’t change between the re-renders, and so React won’t call it unnecessarily.
In this example, the callback ref will be called only when the component mounts and unmounts, since the rendered
<h1>
component stays present throughout any rerenders. If you want to be notified any time a component resizes, you may want to use
ResizeObserver
or a third-party Hook built on it.
If you want, you can extract this logic into a reusable Hook:
function MeasureExample() {
const [rect, ref] = useClientRect(); return (
<>
<h1 ref={ref}>Hello, world</h1>
{rect !== null &&
<h2>The above header is {Math.round(rect.height)}px tall</h2>
}
</>
);
}
function useClientRect() {
const [rect, setRect] = useState(null);
const ref = useCallback(node => {
if (node !== null) {
setRect(node.getBoundingClientRect());
}
}, []);
return [rect, ref];
}
const [thing, setThing] = useState()
mean?
If you’re not familiar with this syntax, check out the explanation in the State Hook documentation.
Yes. See conditionally firing an effect. Note that forgetting to handle updates often introduces bugs, which is why this isn’t the default behavior.
Generally speaking, no.
function Example({ someProp }) {
function doSomething() {
console.log(someProp); }
useEffect(() => {
doSomething();
}, []); // 🔴 This is not safe (it calls `doSomething` which uses `someProp`)}
It’s difficult to remember which props or state are used by functions outside of the effect. This is why usually you’ll want to declare functions needed by an effect inside of it. Then it’s easy to see what values from the component scope that effect depends on:
function Example({ someProp }) {
useEffect(() => {
function doSomething() {
console.log(someProp); }
doSomething();
}, [someProp]); // ✅ OK (our effect only uses `someProp`)}
If after that we still don’t use any values from the component scope, it’s safe to specify
[]
:
useEffect(() => {
function doSomething() {
console.log('hello');
}
doSomething();
}, []); // ✅ OK in this example because we don't use *any* values from component scope
Depending on your use case, there are a few more options described below.
Note
We provide the
exhaustive-deps
ESLint rule as a part of theeslint-plugin-react-hooks
package. It helps you find components that don’t handle updates consistently.
Let’s see why this matters.
If you specify a list of dependencies as the last argument to
useEffect
,
useLayoutEffect
,
useMemo
,
useCallback
, or
useImperativeHandle
, it must include all values that are used inside the callback and participate in the React data flow. That includes props, state, and anything derived from them.
It is only safe to omit a function from the dependency list if nothing in it (or the functions called by it) references props, state, or values derived from them. This example has a bug:
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
async function fetchProduct() {
const response = await fetch('http://myapi/product/' + productId); // Uses productId prop const json = await response.json();
setProduct(json);
}
useEffect(() => {
fetchProduct();
}, []); // 🔴 Invalid because `fetchProduct` uses `productId` // ...
}
The recommended fix is to move that function inside of your effect . That makes it easy to see which props or state your effect uses, and to ensure they’re all declared:
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
useEffect(() => {
// By moving this function inside the effect, we can clearly see the values it uses. async function fetchProduct() { const response = await fetch('http://myapi/product/' + productId); const json = await response.json(); setProduct(json); }
fetchProduct();
}, [productId]); // ✅ Valid because our effect only uses productId // ...
}
This also allows you to handle out-of-order responses with a local variable inside the effect:
useEffect(() => {
let ignore = false; async function fetchProduct() {
const response = await fetch('http://myapi/product/' + productId);
const json = await response.json();
if (!ignore) setProduct(json); }
fetchProduct();
return () => { ignore = true }; }, [productId]);
We moved the function inside the effect so it doesn’t need to be in its dependency list.
Tip
Check out this small demo and this article to learn more about data fetching with Hooks.
If for some reason you can’t move a function inside an effect, there are a few more options:
useCallback
Hook. This ensures it doesn’t change on every render unless
its own
dependencies also change:
function ProductPage({ productId }) {
// ✅ Wrap with useCallback to avoid change on every render const fetchProduct = useCallback(() => { // ... Does something with productId ... }, [productId]); // ✅ All useCallback dependencies are specified
return <ProductDetails fetchProduct={fetchProduct} />;
}
function ProductDetails({ fetchProduct }) {
useEffect(() => {
fetchProduct();
}, [fetchProduct]); // ✅ All useEffect dependencies are specified
// ...
}
Note that in the above example we
need
to keep the function in the dependencies list. This ensures that a change in the
productId
prop of
ProductPage
automatically triggers a refetch in the
ProductDetails
component.
Sometimes, your effect may be using state that changes too often. You might be tempted to omit that state from a list of dependencies, but that usually leads to bugs:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // This effect depends on the `count` state }, 1000);
return () => clearInterval(id);
}, []); // 🔴 Bug: `count` is not specified as a dependency
return <h1>{count}</h1>;
}
The empty set of dependencies,
[]
, means that the effect will only run once when the component mounts, and not on every re-render. The problem is that inside the
setInterval
callback, the value of
count
does not change, because we’ve created a closure with the value of
count
set to
0
as it was when the effect callback ran. Every second, this callback then calls
setCount(0 + 1)
, so the count never goes above 1.
Specifying
[count]
as a list of dependencies would fix the bug, but would cause the interval to be reset on every change. Effectively, each
setInterval
would get one chance to execute before being cleared (similar to a
setTimeout
.) That may not be desirable. To fix this, we can use the functional update form of
setState
. It lets us specify
how
the state needs to change without referencing the
current
state:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1); // ✅ This doesn't depend on `count` variable outside }, 1000);
return () => clearInterval(id);
}, []); // ✅ Our effect doesn't use any variables in the component scope
return <h1>{count}</h1>;
}
(The identity of the
setCount
function is guaranteed to be stable so it’s safe to omit.)
Now, the
setInterval
callback executes once a second, but each time the inner call to
setCount
can use an up-to-date value for
count
(called
c
in the callback here.)
In more complex cases (such as if one state depends on another state), try moving the state update logic outside the effect with the
useReducer
Hook. This article offers an example of how you can do this.
The identity of the
dispatch
function from
useReducer
is always stable
— even if the reducer function is declared inside the component and reads its props.
As a last resort, if you want something like
this
in a class, you can use a ref to hold a mutable variable. Then you can write and read to it. For example:
function Example(props) {
// Keep latest props in a ref. const latestProps = useRef(props); useEffect(() => { latestProps.current = props; });
useEffect(() => {
function tick() {
// Read latest props at any time console.log(latestProps.current); }
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []); // This effect never re-runs}
Only do this if you couldn’t find a better alternative, as relying on mutation makes components less predictable. If there’s a specific pattern that doesn’t translate well, file an issue with a runnable example code and we can try to help.
shouldComponentUpdate
?
You can wrap a function component with
React.memo
to shallowly compare its props:
const Button = React.memo((props) => {
// your component
});
It’s not a Hook because it doesn’t compose like Hooks do.
React.memo
is equivalent to
PureComponent
, but it only compares props. (You can also add a second argument to specify a custom comparison function that takes the old and new props. If it returns true, the update is skipped.)
React.memo
doesn’t compare state because there is no single state object to compare. But you can make children pure too, or even optimize individual children with
useMemo
.
The
useMemo
Hook lets you cache calculations between multiple renders by “remembering” the previous computation:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
This code calls
computeExpensiveValue(a, b)
. But if the dependencies
[a, b]
haven’t changed since the last value,
useMemo
skips calling it a second time and simply reuses the last value it returned.
Remember that the function passed to
useMemo
runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in
useEffect
, not
useMemo
.
You may rely on
useMemo
as a performance optimization, not as a semantic guarantee.
In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without
useMemo
— and then add it to optimize performance. (For rare cases when a value must
never
be recomputed, you can lazily initialize a ref.)
Conveniently,
useMemo
also lets you skip an expensive re-render of a child:
function Parent({ a, b }) {
// Only re-rendered if `a` changes:
const child1 = useMemo(() => <Child1 a={a} />, [a]);
// Only re-rendered if `b` changes:
const child2 = useMemo(() => <Child2 b={b} />, [b]);
return (
<>
{child1}
{child2}
</>
)
}
Note that this approach won’t work in a loop because Hook calls can’t be placed inside loops. But you can extract a separate component for the list item, and call
useMemo
there.
useMemo
lets you memoize an expensive calculation if the dependencies are the same. However, it only serves as a hint, and doesn’t
guarantee
the computation won’t re-run. But sometimes you need to be sure an object is only created once.
The first common use case is when creating the initial state is expensive:
function Table(props) {
// ⚠️ createRows() is called on every render
const [rows, setRows] = useState(createRows(props.count));
// ...
}
To avoid re-creating the ignored initial state, we can pass a
function
to
useState
:
function Table(props) {
// ✅ createRows() is only called once
const [rows, setRows] = useState(() => createRows(props.count));
// ...
}
React will only call this function during the first render. See the
useState
API reference.
You might also occasionally want to avoid re-creating the
useRef()
initial value.
For example, maybe you want to ensure some imperative class instance only gets created once:
function Image(props) {
// ⚠️ IntersectionObserver is created on every render
const ref = useRef(new IntersectionObserver(onIntersect));
// ...
}
useRef
does not
accept a special function overload like
useState
. Instead, you can write your own function that creates and sets it lazily:
function Image(props) {
const ref = useRef(null);
// ✅ IntersectionObserver is created lazily once
function getObserver() {
if (ref.current === null) {
ref.current = new IntersectionObserver(onIntersect);
}
return ref.current;
}
// When you need it, call getObserver()
// ...
}
This avoids creating an expensive object until it’s truly needed for the first time. If you use Flow or TypeScript, you can also give
getObserver()
a non-nullable type for convenience.
No. In modern browsers, the raw performance of closures compared to classes doesn’t differ significantly except in extreme scenarios.
In addition, consider that the design of Hooks is more efficient in a couple ways:
Traditionally, performance concerns around inline functions in React have been related to how passing new callbacks on each render breaks
shouldComponentUpdate
optimizations in child components. Hooks approach this problem from three sides.
The
useCallback
Hook lets you keep the same callback reference between re-renders so that
shouldComponentUpdate
continues to work:
// Will not change unless `a` or `b` changes
const memoizedCallback = useCallback(() => { doSomething(a, b);
}, [a, b]);
useMemo
Hook makes it easier to control when individual children update, reducing the need for pure components.
useReducer
Hook reduces the need to pass callbacks deeply, as explained below.
We’ve found that most people don’t enjoy manually passing callbacks through every level of a component tree. Even though it is more explicit, it can feel like a lot of “plumbing”.
In large component trees, an alternative we recommend is to pass down a
dispatch
function from
useReducer
via context:
const TodosDispatch = React.createContext(null);
function TodosApp() {
// Note: `dispatch` won't change between re-renders const [todos, dispatch] = useReducer(todosReducer);
return (
<TodosDispatch.Provider value={dispatch}>
<DeepTree todos={todos} />
</TodosDispatch.Provider>
);
}
Any child in the tree inside
TodosApp
can use the
dispatch
function to pass actions up to
TodosApp
:
function DeepChild(props) {
// If we want to perform an action, we can get dispatch from context. const dispatch = useContext(TodosDispatch);
function handleClick() {
dispatch({ type: 'add', text: 'hello' });
}
return (
<button onClick={handleClick}>Add todo</button>
);
}
This is both more convenient from the maintenance perspective (no need to keep forwarding callbacks), and avoids the callback problem altogether. Passing
dispatch
down like this is the recommended pattern for deep updates.
Note that you can still choose whether to pass the application
state
down as props (more explicit) or as context (more convenient for very deep updates). If you use context to pass down the state too, use two different context types — the
dispatch
context never changes, so components that read it don’t need to rerender unless they also need the application state.
useCallback
?
Note
We recommend to pass
dispatch
down in context rather than individual callbacks in props. The approach below is only mentioned here for completeness and as an escape hatch.
In some rare cases you might need to memoize a callback with
useCallback
but the memoization doesn’t work very well because the inner function has to be re-created too often. If the function you’re memoizing is an event handler and isn’t used during rendering, you can use ref as an instance variable, and save the last committed value into it manually:
function Form() {
const [text, updateText] = useState('');
const textRef = useRef();
useEffect(() => {
textRef.current = text; // Write it to the ref });
const handleSubmit = useCallback(() => {
const currentText = textRef.current; // Read it from the ref alert(currentText);
}, [textRef]); // Don't recreate handleSubmit like [text] would do
return (
<>
<input value={text} onChange={e => updateText(e.target.value)} />
<ExpensiveTree onSubmit={handleSubmit} />
</>
);
}
This is a rather convoluted pattern but it shows that you can do this escape hatch optimization if you need it. It’s more bearable if you extract it to a custom Hook:
function Form() {
const [text, updateText] = useState('');
// Will be memoized even if `text` changes:
const handleSubmit = useEventCallback(() => { alert(text);
}, [text]);
return (
<>
<input value={text} onChange={e => updateText(e.target.value)} />
<ExpensiveTree onSubmit={handleSubmit} />
</>
);
}
function useEventCallback(fn, dependencies) { const ref = useRef(() => {
throw new Error('Cannot call an event handler while rendering.');
});
useEffect(() => {
ref.current = fn;
}, [fn, ...dependencies]);
return useCallback(() => {
const fn = ref.current;
return fn();
}, [ref]);
}
In either case, we don’t recommend this pattern and only show it here for completeness. Instead, it is preferable to avoid passing callbacks deep down.
React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).
There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like
useState()
, it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple
useState()
calls each get independent local state.
Hooks synthesize ideas from several different sources:
adopt
keyword proposal as a sugar syntax for render props.
Sebastian Markbåge came up with the original design for Hooks, later refined by Andrew Clark, Sophie Alpert, Dominic Gannaway, and other members of the React team.
You can test React components similar to testing other JavaScript code.
There are a few ways to test React components. Broadly, they divide into two categories:
This documentation section focuses on testing strategies for the first case. While full end-to-end tests can be very useful to prevent regressions to important workflows, such tests are not concerned with React components in particular, and are out of the scope of this section.
When choosing testing tools, it is worth considering a few tradeoffs:
Different answers may work for different teams and products.
Jest
is a JavaScript test runner that lets you access the DOM via
jsdom
. While jsdom is only an approximation of how the browser works, it is often good enough for testing React components. Jest provides a great iteration speed combined with powerful features like mocking modules and timers so you can have more control over how the code executes.
React Testing Library is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn’t provide a way to “shallowly” render a component without its children, a test runner like Jest lets you do this by mocking.
This section is divided in two pages:
Common testing patterns for React components.
Note:
This page assumes you’re using Jest as a test runner. If you use a different test runner, you may need to adjust the API, but the overall shape of the solution will likely be the same. Read more details on setting up a testing environment on the Testing Environments page.
On this page, we will primarily use function components. However, these testing strategies don’t depend on implementation details, and work just as well for class components too.
act()
For each test, we usually want to render our React tree to a DOM element that’s attached to
document
. This is important so that it can receive DOM events. When the test ends, we want to “clean up” and unmount the tree from the
document
.
A common way to do it is to use a pair of
beforeEach
and
afterEach
blocks so that they’ll always run and isolate the effects of a test to itself:
import { unmountComponentAtNode } from "react-dom";
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
You may use a different pattern, but keep in mind that we want to execute the cleanup even if a test fails . Otherwise, tests can become “leaky”, and one test can change the behavior of another test. That makes them difficult to debug.
act()
When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface.
react-dom/test-utils
provides a helper called
act()
that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions:
act(() => {
// render components
});
// make assertions
This helps make your tests run closer to what real users would experience when using your application. The rest of these examples use
act()
to make these guarantees.
You might find using
act()
directly a bit too verbose. To avoid some of the boilerplate, you could use a library like React Testing Library, whose helpers are wrapped with
act()
.
Note:
The name
act
comes from the Arrange-Act-Assert pattern.
Commonly, you might want to test whether a component renders correctly for given props. Consider a simple component that renders a message based on a prop:
// hello.js
import React from "react";
export default function Hello(props) {
if (props.name) {
return <h1>Hello, {props.name}!</h1>;
} else {
return <span>Hey, stranger</span>;
}
}
We can write a test for this component:
// hello.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Hello from "./hello";
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("renders with or without a name", () => {
act(() => { render(<Hello />, container); }); expect(container.textContent).toBe("Hey, stranger");
act(() => {
render(<Hello name="Jenny" />, container);
});
expect(container.textContent).toBe("Hello, Jenny!");
act(() => {
render(<Hello name="Margaret" />, container);
});
expect(container.textContent).toBe("Hello, Margaret!");
});
Instead of calling real APIs in all your tests, you can mock requests with dummy data. Mocking data fetching with “fake” data prevents flaky tests due to an unavailable backend, and makes them run faster. Note: you may still want to run a subset of tests using an “end-to-end” framework that tells whether the whole app is working together.
// user.js
import React, { useState, useEffect } from "react";
export default function User(props) {
const [user, setUser] = useState(null);
async function fetchUserData(id) {
const response = await fetch("/" + id);
setUser(await response.json());
}
useEffect(() => {
fetchUserData(props.id);
}, [props.id]);
if (!user) {
return "loading...";
}
return (
<details>
<summary>{user.name}</summary>
<strong>{user.age}</strong> years old
<br />
lives in {user.address}
</details>
);
}
We can write tests for it:
// user.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import User from "./user";
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("renders user data", async () => {
const fakeUser = { name: "Joni Baez", age: "32", address: "123, Charming Avenue" }; jest.spyOn(global, "fetch").mockImplementation(() => Promise.resolve({ json: () => Promise.resolve(fakeUser) }) );
// Use the asynchronous version of act to apply resolved promises
await act(async () => {
render(<User id="123" />, container);
});
expect(container.querySelector("summary").textContent).toBe(fakeUser.name);
expect(container.querySelector("strong").textContent).toBe(fakeUser.age);
expect(container.textContent).toContain(fakeUser.address);
// remove the mock to ensure tests are completely isolated global.fetch.mockRestore();});
Some modules might not work well inside a testing environment, or may not be as essential to the test itself. Mocking out these modules with dummy replacements can make it easier to write tests for your own code.
Consider a
Contact
component that embeds a third-party
GoogleMap
component:
// map.js
import React from "react";
import { LoadScript, GoogleMap } from "react-google-maps";
export default function Map(props) {
return (
<LoadScript id="script-loader" googleMapsApiKey="YOUR_API_KEY">
<GoogleMap id="example-map" center={props.center} />
</LoadScript>
);
}
// contact.js
import React from "react";
import Map from "./map";
export default function Contact(props) {
return (
<div>
<address>
Contact {props.name} via{" "}
<a data-testid="email" href={"mailto:" + props.email}>
email
</a>
or on their <a data-testid="site" href={props.site}>
website
</a>.
</address>
<Map center={props.center} />
</div>
);
}
If we don’t want to load this component in our tests, we can mock out the dependency itself to a dummy component, and run our tests:
// contact.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Contact from "./contact";
import MockedMap from "./map";
jest.mock("./map", () => { return function DummyMap(props) { return ( <div data-testid="map"> {props.center.lat}:{props.center.long} </div> ); };});
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("should render contact information", () => {
const center = { lat: 0, long: 0 };
act(() => {
render(
<Contact
name="Joni Baez"
email="test@example.com"
site="http://test.com"
center={center}
/>,
container
);
});
expect(
container.querySelector("[data-testid='email']").getAttribute("href")
).toEqual("mailto:test@example.com");
expect(
container.querySelector('[data-testid="site"]').getAttribute("href")
).toEqual("http://test.com");
expect(container.querySelector('[data-testid="map"]').textContent).toEqual(
"0:0"
);
});
We recommend dispatching real DOM events on DOM elements, and then asserting on the result. Consider a
Toggle
component:
// toggle.js
import React, { useState } from "react";
export default function Toggle(props) {
const [state, setState] = useState(false);
return (
<button
onClick={() => {
setState(previousState => !previousState);
props.onChange(!state);
}}
data-testid="toggle"
>
{state === true ? "Turn off" : "Turn on"}
</button>
);
}
We could write tests for it:
// toggle.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Toggle from "./toggle";
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("changes value when clicked", () => {
const onChange = jest.fn();
act(() => {
render(<Toggle onChange={onChange} />, container);
});
// get a hold of the button element, and trigger some clicks on it
const button = document.querySelector("[data-testid=toggle]");
expect(button.innerHTML).toBe("Turn on");
act(() => {
button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
expect(onChange).toHaveBeenCalledTimes(1);
expect(button.innerHTML).toBe("Turn off");
act(() => {
for (let i = 0; i < 5; i++) {
button.dispatchEvent(new MouseEvent("click", { bubbles: true }));
} });
expect(onChange).toHaveBeenCalledTimes(6);
expect(button.innerHTML).toBe("Turn on");
});
Different DOM events and their properties are described in MDN. Note that you need to pass
{ bubbles: true }
in each event you create for it to reach the React listener because React automatically delegates events to the root.
Note:
React Testing Library offers a more concise helper for firing events.
Your code might use timer-based functions like
setTimeout
to schedule more work in the future. In this example, a multiple choice panel waits for a selection and advances, timing out if a selection isn’t made in 5 seconds:
// card.js
import React, { useEffect } from "react";
export default function Card(props) {
useEffect(() => {
const timeoutID = setTimeout(() => {
props.onSelect(null);
}, 5000);
return () => {
clearTimeout(timeoutID);
};
}, [props.onSelect]);
return [1, 2, 3, 4].map(choice => (
<button
key={choice}
data-testid={choice}
onClick={() => props.onSelect(choice)}
>
{choice}
</button>
));
}
We can write tests for this component by leveraging Jest’s timer mocks, and testing the different states it can be in.
// card.test.js
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import Card from "./card";
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
jest.useFakeTimers();
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
jest.useRealTimers();
});
it("should select null after timing out", () => {
const onSelect = jest.fn();
act(() => {
render(<Card onSelect={onSelect} />, container);
});
// move ahead in time by 100ms act(() => {
jest.advanceTimersByTime(100);
});
expect(onSelect).not.toHaveBeenCalled();
// and then move ahead by 5 seconds act(() => {
jest.advanceTimersByTime(5000);
});
expect(onSelect).toHaveBeenCalledWith(null);
});
it("should cleanup on being removed", () => {
const onSelect = jest.fn();
act(() => {
render(<Card onSelect={onSelect} />, container);
});
act(() => {
jest.advanceTimersByTime(100);
});
expect(onSelect).not.toHaveBeenCalled();
// unmount the app
act(() => {
render(null, container);
});
act(() => {
jest.advanceTimersByTime(5000);
});
expect(onSelect).not.toHaveBeenCalled();
});
it("should accept selections", () => {
const onSelect = jest.fn();
act(() => {
render(<Card onSelect={onSelect} />, container);
});
act(() => {
container
.querySelector("[data-testid='2']")
.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
expect(onSelect).toHaveBeenCalledWith(2);
});
You can use fake timers only in some tests. Above, we enabled them by calling
jest.useFakeTimers()
. The main advantage they provide is that your test doesn’t actually have to wait five seconds to execute, and you also didn’t need to make the component code more convoluted just for testing.
Frameworks like Jest also let you save “snapshots” of data with
toMatchSnapshot
/
toMatchInlineSnapshot
. With these, we can “save” the rendered component output and ensure that a change to it has to be explicitly committed as a change to the snapshot.
In this example, we render a component and format the rendered HTML with the
pretty
package, before saving it as an inline snapshot:
// hello.test.js, again
import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";
import pretty from "pretty";
import Hello from "./hello";
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
it("should render a greeting", () => {
act(() => {
render(<Hello />, container);
});
expect( pretty(container.innerHTML) ).toMatchInlineSnapshot(); /* ... gets filled automatically by jest ... */
act(() => {
render(<Hello name="Jenny" />, container);
});
expect(
pretty(container.innerHTML)
).toMatchInlineSnapshot(); /* ... gets filled automatically by jest ... */
act(() => {
render(<Hello name="Margaret" />, container);
});
expect(
pretty(container.innerHTML)
).toMatchInlineSnapshot(); /* ... gets filled automatically by jest ... */
});
It’s typically better to make more specific assertions than to use snapshots. These kinds of tests include implementation details so they break easily, and teams can get desensitized to snapshot breakages. Selectively mocking some child components can help reduce the size of snapshots and keep them readable for the code review.
In rare cases, you may be running a test on a component that uses multiple renderers. For example, you may be running snapshot tests on a component with
react-test-renderer
, that internally uses
render
from
react-dom
inside a child component to render some content. In this scenario, you can wrap updates with
act()
s corresponding to their renderers.
import { act as domAct } from "react-dom/test-utils";
import { act as testAct, create } from "react-test-renderer";
// ...
let root;
domAct(() => {
testAct(() => {
root = create(<App />);
});
});
expect(root).toMatchSnapshot();
If some common scenario is not covered, please let us know on the issue tracker for the documentation website.
This document goes through the factors that can affect your environment and recommendations for some scenarios.
Test runners like Jest, mocha, ava let you write test suites as regular JavaScript, and run them as part of your development process. Additionally, test suites are run as part of continuous integration.
jsdom
support.
If you use Create React App, Jest is already included out of the box with useful defaults.
Tests often run in an environment without access to a real rendering surface like a browser. For these environments, we recommend simulating a browser with
jsdom
, a lightweight browser implementation that runs inside Node.js.
In most cases, jsdom behaves like a regular browser would, but doesn’t have features like layout and navigation. This is still useful for most web-based component tests, since it runs quicker than having to start up a browser for each test. It also runs in the same process as your tests, so you can write code to examine and assert on the rendered DOM.
Just like in a real browser, jsdom lets us model user interactions; tests can dispatch events on DOM nodes, and then observe and assert on the side effects of these actions (example) .
A large portion of UI tests can be written with the above setup: using Jest as a test runner, rendered to jsdom, with user interactions specified as sequences of browser events, powered by the
act()
helper
(example)
. For example, a lot of React’s own tests are written with this combination.
If you’re writing a library that tests mostly browser-specific behavior, and requires native browser behavior like layout or real inputs, you could use a framework like mocha.
In an environment where you
can’t
simulate a DOM (e.g. testing React Native components on Node.js), you could use event simulation helpers to simulate interactions with elements. Alternately, you could use the
fireEvent
helper from
@testing-library/react-native
.
Frameworks like Cypress, puppeteer and webdriver are useful for running end-to-end tests.
When writing tests, we’d like to mock out the parts of our code that don’t have equivalents inside our testing environment (e.g. checking
navigator.onLine
status inside Node.js). Tests could also spy on some functions, and observe how other parts of the test interact with them. It is then useful to be able to selectively mock these functions with test-friendly versions.
This is especially useful for data fetching. It is usually preferable to use “fake” data for tests to avoid the slowness and flakiness due to fetching from real API endpoints (example) . This helps make the tests predictable. Libraries like Jest and sinon, among others, support mocked functions. For end-to-end tests, mocking network can be more difficult, but you might also want to test the real API endpoints in them anyway.
Some components have dependencies for modules that may not work well in test environments, or aren’t essential to our tests. It can be useful to selectively mock these modules out with suitable replacements (example) .
On Node.js, runners like Jest support mocking modules. You could also use libraries like
mock-require
.
Components might be using time-based functions like
setTimeout
,
setInterval
, or
Date.now
. In testing environments, it can be helpful to mock these functions out with replacements that let you manually “advance” time. This is great for making sure your tests run fast! Tests that are dependent on timers would still resolve in order, but quicker
(example)
. Most frameworks, including Jest, sinon and lolex, let you mock timers in your tests.
Sometimes, you may not want to mock timers. For example, maybe you’re testing an animation, or interacting with an endpoint that’s sensitive to timing (like an API rate limiter). Libraries with timer mocks let you enable and disable them on a per test/suite basis, so you can explicitly choose how these tests would run.
End-to-end tests are useful for testing longer workflows, especially when they’re critical to your business (such as payments or signups). For these tests, you’d probably want to test how a real browser renders the whole app, fetches data from the real API endpoints, uses sessions and cookies, navigates between different links. You might also likely want to make assertions not just on the DOM state, but on the backing data as well (e.g. to verify whether the updates have been persisted to the database).
In this scenario, you would use a framework like Cypress, Playwright or a library like Puppeteer so you can navigate between multiple routes and assert on side effects not just in the browser, but potentially on the backend as well.
React is one of Facebook’s first open source projects that is both under very active development and is also being used to ship code to everybody on facebook.com. We’re still working out the kinks to make contributing to this project as easy and transparent as possible, but we’re not quite there yet. Hopefully this document makes the process for contributing clear and answers some questions that you may have.
Facebook has adopted the Contributor Covenant as its Code of Conduct, and we expect project participants to adhere to it. Please read the full text so that you can understand what actions will and will not be tolerated.
All work on React happens directly on GitHub. Both core team members and external contributors send pull requests which go through the same review process.
React follows semantic versioning. We release patch versions for critical bugfixes, minor versions for new features or non-essential changes, and major versions for any breaking changes. When we make breaking changes, we also introduce deprecation warnings in a minor version so that our users learn about the upcoming changes and migrate their code in advance. Learn more about our commitment to stability and incremental migration in our versioning policy.
Every significant change is documented in the changelog file.
Submit all changes directly to the
main branch
. We don’t use separate branches for development or for upcoming releases. We do our best to keep
main
in good shape, with all tests passing.
Code that lands in
main
must be compatible with the latest stable release. It may contain additional features, but no breaking changes. We should be able to release a new minor version from the tip of
main
at any time.
To keep the
main
branch in a releasable state, breaking changes and experimental features must be gated behind a feature flag.
Feature flags are defined in
packages/shared/ReactFeatureFlags.js
. Some builds of React may enable different sets of feature flags; for example, the React Native build may be configured differently than React DOM. These flags are found in
packages/shared/forks
. Feature flags are statically typed by Flow, so you can run
yarn flow
to confirm that you’ve updated all the necessary files.
React’s build system will strip out disabled feature branches before publishing. A continuous integration job runs on every commit to check for changes in bundle size. You can use the change in size as a signal that a feature was gated correctly.
We are using GitHub Issues for our public bugs. We keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new task, try to make sure your problem doesn’t already exist.
The best way to get your bug fixed is to provide a reduced test case. This JSFiddle template is a great starting point.
Facebook has a bounty program for the safe disclosure of security bugs. With that in mind, please do not file public issues; go through the process outlined on that page.
There is also an active community of React users on the Discord chat platform in case you need help with React.
If you intend to change the public API, or make any non-trivial changes to the implementation, we recommend filing an issue. This lets us reach an agreement on your proposal before you put significant effort into it.
If you’re only fixing a bug, it’s fine to submit a pull request right away but we still recommend to file an issue detailing what you’re fixing. This is helpful in case we don’t accept that specific fix but want to keep track of the issue.
Working on your first Pull Request? You can learn how from this free video series:
How to Contribute to an Open Source Project on GitHub
To help you get your feet wet and get you familiar with our contribution process, we have a list of good first issues that contain bugs that have a relatively limited scope. This is a great place to get started.
If you decide to fix an issue, please be sure to check the comment thread in case somebody is already working on a fix. If nobody is working on it at the moment, please leave a comment stating that you intend to work on it so other people don’t accidentally duplicate your effort.
If somebody claims an issue but doesn’t follow up for more than two weeks, it’s fine to take it over but you should still leave a comment.
The core team is monitoring for pull requests. We will review your pull request and either merge it, request changes to it, or close it with an explanation. For API changes we may need to fix our internal uses at Facebook.com, which could cause some delay. We’ll do our best to provide updates and feedback throughout the process.
Before submitting a pull request, please make sure the following is done:
main
.
yarn
in the repository root.
yarn test
). Tip:
yarn test --watch TestName
is helpful in development.
yarn test --prod
to test in the production environment.
yarn debug-test --watch TestName
, open
chrome://inspect
, and press “Inspect”.
yarn prettier
).
yarn lint
). Tip:
yarn linc
to only check changed files.
yarn flow
).
In order to accept your pull request, we need you to submit a CLA. You only need to do this once, so if you’ve done this for another Facebook open source project, you’re good to go. If you are submitting a pull request for the first time, just let us know that you have completed the CLA and we can cross-check with your GitHub username.
Complete your CLA here.
gcc
installed or are comfortable installing a compiler if needed. Some of our dependencies may require a compilation step. On OS X, the Xcode Command Line Tools will cover this. On Ubuntu,
apt-get install build-essential
will install the required packages. Similar commands should work on other Linux distros. Windows will require some additional steps, see the
node-gyp
installation instructions for details.
After cloning React, run
yarn
to fetch its dependencies.
Then, you can run several commands:
yarn lint
checks the code style.
yarn linc
is like
yarn lint
but faster because it only checks files that differ in your branch.
yarn test
runs the complete test suite.
yarn test --watch
runs an interactive test watcher.
yarn test --prod
runs tests in the production environment.
yarn test <pattern>
runs tests with matching filenames.
yarn debug-test
is just like
yarn test
but with a debugger. Open
chrome://inspect
and press “Inspect”.
yarn flow
runs the Flow typechecks.
yarn build
creates a
build
folder with all the packages.
yarn build react/index,react-dom/index --type=UMD
creates UMD builds of just React and ReactDOM.
We recommend running
yarn test
(or its variations above) to make sure you don’t introduce any regressions as you work on your change. However, it can be handy to try your build of React in a real project.
First, run
yarn build
. This will produce pre-built bundles in
build
folder, as well as prepare npm packages inside
build/packages
.
The easiest way to try your changes is to run
yarn build react/index,react-dom/index --type=UMD
and then open
fixtures/packaging/babel-standalone/dev.html
. This file already uses
react.development.js
from the
build
folder so it will pick up your changes.
If you want to try your changes in your existing React project, you may copy
build/node_modules/react/umd/react.development.js
,
build/node_modules/react-dom/umd/react-dom.development.js
, or any other build products into your app and use them instead of the stable version.
If your project uses React from npm, you may delete
react
and
react-dom
in its dependencies and use
yarn link
to point them to your local
build
folder. Note that
instead of
--type=UMD
you’ll want to pass
--type=NODE
when building
. You’ll also need to build the
scheduler
package:
cd ~/path_to_your_react_clone/
yarn build react/index,react/jsx,react-dom/index,scheduler --type=NODE
cd build/node_modules/react
yarn link
cd build/node_modules/react-dom
yarn link
cd ~/path/to/your/project
yarn link react react-dom
Every time you run
yarn build
in the React folder, the updated versions will appear in your project’s
node_modules
. You can then rebuild your project to try your changes.
If some package is still missing (e.g. maybe you use
react-dom/server
in your project), you can always do a full build with
yarn build
. Note that running
yarn build
without options takes a long time.
We still require that your pull request contains unit tests for any new functionality. This way we can ensure that we don’t break your code in the future.
We use an automatic code formatter called Prettier.
Run
yarn prettier
after making any changes to the code.
Then, our linter will catch most issues that may exist in your code.
You can check the status of your code styling by simply running
yarn linc
.
However, there are still some styles that the linter cannot pick up. If you are unsure about something, looking at Airbnb’s Style Guide will guide you in the right direction.
Many changes, including bug fixes and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow.
Some changes though are “substantial”, and we ask that these be put through a bit of a design process and produce a consensus among the React core team.
The “RFC” (request for comments) process is intended to provide a consistent and controlled path for new features to enter the project. You can contribute by visiting the rfcs repository.
By contributing to React, you agree that your contributions will be licensed under its MIT license.
Read the next section to learn how the codebase is organized.
This section will give you an overview of the React codebase organization, its conventions, and the implementation.
If you want to contribute to React we hope that this guide will help you feel more comfortable making changes.
We don’t necessarily recommend any of these conventions in React apps. Many of them exist for historical reasons and might change with time.
After cloning the React repository, you will see a few top-level folders in it:
packages
contains metadata (such as
package.json
) and the source code (
src
subdirectory) for all packages in the React repository.
If your change is related to the code, the
src
subdirectory of each package is where you’ll spend most of your time.
fixtures
contains a few small React test applications for contributors.
build
is the build output of React. It is not in the repository but it will appear in your React clone after you build it for the first time.
The documentation is hosted in a separate repository from React.
There are a few other top-level folders but they are mostly used for the tooling and you likely won’t ever encounter them when contributing.
We don’t have a top-level directory for unit tests. Instead, we put them into a directory called
__tests__
relative to the files that they test.
For example, a test for
setInnerHTML.js
is located in
__tests__/setInnerHTML-test.js
right next to it.
The React codebase uses
console.error
to display warnings:
if (__DEV__) {
console.error('Something is wrong.');
}
Warnings are only enabled in development. In production, they are completely stripped out. If you need to forbid some code path from executing, use
invariant
module instead:
var invariant = require('invariant');
invariant(
2 + 2 === 4,
'You shall not pass!'
);
The invariant is thrown when the
invariant
condition is
false
.
“Invariant” is just a way of saying “this condition always holds true”. You can think about it as making an assertion.
It is important to keep development and production behavior similar, so
invariant
throws both in development and in production. The error messages are automatically replaced with error codes in production to avoid negatively affecting the byte size.
You can use
__DEV__
pseudo-global variable in the codebase to guard development-only blocks of code.
It is inlined during the compile step, and turns into
process.env.NODE_ENV !== 'production'
checks in the CommonJS builds.
For standalone builds, it becomes
true
in the unminified build, and gets completely stripped out with the
if
blocks it guards in the minified build.
if (__DEV__) {
// This code will only run in development.
}
We recently started introducing Flow checks to the codebase. Files marked with the
@flow
annotation in the license header comment are being typechecked.
We accept pull requests adding Flow annotations to existing code. Flow annotations look like this:
ReactRef.detachRefs = function(
instance: ReactInstance,
element: ReactElement | string | number | null | false,
): void {
// ...
}
When possible, new code should use Flow annotations.
You can run
yarn flow
locally to check your code with Flow.
React is a monorepo. Its repository contains multiple separate packages so that their changes can be coordinated together, and issues live in one place.
The “core” of React includes all the top-level
React
APIs, for example:
React.createElement()
React.Component
React.Children
React core only includes the APIs necessary to define components. It does not include the reconciliation algorithm or any platform-specific code. It is used both by React DOM and React Native components.
The code for React core is located in
packages/react
in the source tree. It is available on npm as the
react
package. The corresponding standalone browser build is called
react.js
, and it exports a global called
React
.
React was originally created for the DOM but it was later adapted to also support native platforms with React Native. This introduced the concept of “renderers” to React internals.
Renderers manage how a React tree turns into the underlying platform calls.
Renderers are also located in
packages/
:
ReactDOM
APIs and is available as
react-dom
npm package. It can also be used as standalone browser bundle called
react-dom.js
that exports a
ReactDOM
global.
The only other officially supported renderer is
react-art
. It used to be in a separate GitHub repository but we moved it into the main source tree for now.
Note:
Technically the
react-native-renderer
is a very thin layer that teaches React to interact with React Native implementation. The real platform-specific code managing the native views lives in the React Native repository together with its components.
Even vastly different renderers like React DOM and React Native need to share a lot of logic. In particular, the reconciliation algorithm should be as similar as possible so that declarative rendering, custom components, state, lifecycle methods, and refs work consistently across platforms.
To solve this, different renderers share some code between them. We call this part of React a “reconciler”. When an update such as
setState()
is scheduled, the reconciler calls
render()
on components in the tree and mounts, updates, or unmounts them.
Reconcilers are not packaged separately because they currently have no public API. Instead, they are exclusively used by renderers such as React DOM and React Native.
The “stack” reconciler is the implementation powering React 15 and earlier. We have since stopped using it, but it is documented in detail in the next section.
The “fiber” reconciler is a new effort aiming to resolve the problems inherent in the stack reconciler and fix a few long-standing issues. It has been the default reconciler since React 16.
Its main goals are:
render()
.
You can read more about React Fiber Architecture here and here. While it has shipped with React 16, the async features are not enabled by default yet.
Its source code is located in
packages/react-reconciler
.
React implements a layer over native events to smooth out cross-browser differences. Its source code is located in
packages/react-dom/src/events
.
Read the next section to learn about the pre-React 16 implementation of reconciler in more detail. We haven’t documented the internals of the new reconciler yet.