Write usePrevious(value) and export it from usePrevious.js. It returns what value was before it last changed, so a component can show both, like a tea's price going from 4 to 5.
On the first render there's no earlier value, so it returns undefined. When a render brings a new value, it returns the one before it.
Renders that don't change value don't count. If the component renders again for another reason, say the customer stars the tea, usePrevious keeps returning the same earlier value. Compare values with Object.is, as React does, so two different objects with the same contents count as a change.
function PriceTag({ price }) {
const before = usePrevious(price);
return (
<p>
Now {price}
{before !== undefined && <span>, was {before}</span>}
</p>
);
}
The Preview shows Demo.jsx, a price tag that uses your hook.
const { result } = renderHook(() => usePrevious(4));
expect(result.current).toBeUndefined();
const { result, rerender } = renderHook(({ value }) => usePrevious(value), { initialProps: { value: 4 } });
rerender({ value: 5 });
expect(result.current).toBe(4);
const { result, rerender } = renderHook(({ value }) => usePrevious(value), { initialProps: { value: 4 } });
rerender({ value: 5 });
rerender({ value: 6 });
expect(result.current).toBe(5);
rerender({ value: 4 });
expect(result.current).toBe(6);
const { result, rerender } = renderHook(({ value }) => usePrevious(value), { initialProps: { value: 4 } });
rerender({ value: 4 });
rerender({ value: 4 });
expect(result.current).toBeUndefined();
const { result, rerender } = renderHook(({ value }) => usePrevious(value), { initialProps: { value: 4 } });
rerender({ value: 5 });
rerender({ value: 5 });
rerender({ value: 5 });
expect(result.current).toBe(4);
const first = { name: "Green" };
const second = { name: "Green" };
const { result, rerender } = renderHook(({ value }) => usePrevious(value), { initialProps: { value: first } });
rerender({ value: second });
expect(result.current).toBe(first);
function PriceTag() {
const [price, setPrice] = React.useState(4);
const [starred, setStarred] = React.useState(false);
const before = usePrevious(price);
return (
<div>
<p>Price: {price}</p>
<p>Before: {before === undefined ? "none" : before}</p>
<button onClick={() => setPrice(price + 1)}>Raise the price</button>
<button onClick={() => setStarred(!starred)}>{starred ? "Unstar" : "Star"}</button>
</div>
);
}
render(<PriceTag />);
expect(screen.getByText("Before: none")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Raise the price" }));
expect(screen.getByText("Price: 5")).toBeInTheDocument();
expect(screen.getByText("Before: 4")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Star" }));
expect(screen.getByText("Before: 4")).toBeInTheDocument();
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with usePrevious. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.