diff --git a/docs/useCookie.md b/docs/useCookie.md
new file mode 100644
index 00000000..92bf9f63
--- /dev/null
+++ b/docs/useCookie.md
@@ -0,0 +1,39 @@
+# `useCookie`
+
+React hook that returns the current value of a `cookie`, a callback to update the `cookie`
+and a callback to delete the `cookie.`
+
+## Usage
+
+```jsx
+import { useCookie } from "react-use";
+
+const Demo = () => {
+ const [value, updateCookie, deleteCookie] = useCookie("my-cookie");
+ const [counter, setCounter] = useState(1);
+
+ useEffect(() => {
+ deleteCookie();
+ }, []);
+
+ const updateCookieHandler = () => {
+ updateCookie(`my-awesome-cookie-${counter}`);
+ setCounter(c => c + 1);
+ };
+
+ return (
+
+
Value: {value}
+
+
+
+
+ );
+};
+```
+
+## Reference
+
+```ts
+const [value, updateCookie, deleteCookie] = useCookie(cookieName: string);
+```
diff --git a/package.json b/package.json
index 5c6dbeed..b8330c07 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"@xobotyi/scrollbar-width": "1.5.0",
"copy-to-clipboard": "^3.2.0",
"fast-shallow-equal": "^1.0.0",
+ "js-cookie": "^2.2.1",
"nano-css": "^5.2.1",
"react-fast-compare": "^2.0.4",
"resize-observer-polyfill": "^1.5.1",
@@ -79,6 +80,7 @@
"@storybook/react": "5.3.1",
"@testing-library/react-hooks": "3.2.1",
"@types/jest": "24.0.25",
+ "@types/js-cookie": "^2.2.4",
"@types/react": "16.9.11",
"babel-core": "6.26.3",
"babel-loader": "8.0.6",
diff --git a/src/index.ts b/src/index.ts
index 4a0aee4f..dc6f65ff 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -8,6 +8,7 @@ export { default as useBattery } from './useBattery';
export { default as useBeforeUnload } from './useBeforeUnload';
export { default as useBoolean } from './useBoolean';
export { default as useClickAway } from './useClickAway';
+export { default as useCookie } from './useCookie';
export { default as useCopyToClipboard } from './useCopyToClipboard';
export { default as useCounter } from './useCounter';
export { default as useCss } from './useCss';
diff --git a/src/useCookie.ts b/src/useCookie.ts
new file mode 100644
index 00000000..d8150088
--- /dev/null
+++ b/src/useCookie.ts
@@ -0,0 +1,25 @@
+import { useState, useCallback } from 'react';
+import Cookies from 'js-cookie';
+
+const useCookie = (
+ cookieName: string
+): [string | null, (newValue: string, options?: Cookies.CookieAttributes) => void, () => void] => {
+ const [value, setValue] = useState(() => Cookies.get(cookieName) || null);
+
+ const updateCookie = useCallback(
+ (newValue: string, options?: Cookies.CookieAttributes) => {
+ Cookies.set(cookieName, newValue, options);
+ setValue(newValue);
+ },
+ [cookieName]
+ );
+
+ const deleteCookie = useCallback(() => {
+ Cookies.remove(cookieName);
+ setValue(null);
+ }, [cookieName]);
+
+ return [value, updateCookie, deleteCookie];
+};
+
+export default useCookie;
diff --git a/stories/useCookie.story.tsx b/stories/useCookie.story.tsx
new file mode 100644
index 00000000..89d3f753
--- /dev/null
+++ b/stories/useCookie.story.tsx
@@ -0,0 +1,31 @@
+import { storiesOf } from "@storybook/react";
+import React, { useState, useEffect } from "react";
+import { useCookie } from "../src";
+import ShowDocs from "./util/ShowDocs";
+
+const Demo = () => {
+ const [value, updateCookie, deleteCookie] = useCookie("my-cookie");
+ const [counter, setCounter] = useState(1);
+
+ useEffect(() => {
+ deleteCookie();
+ }, []);
+
+ const updateCookieHandler = () => {
+ updateCookie(`my-awesome-cookie-${counter}`);
+ setCounter(c => c + 1);
+ };
+
+ return (
+
+
Value: {value}
+
+
+
+
+ );
+};
+
+storiesOf("Side effects|useCookie", module)
+ .add("Docs", () => )
+ .add("Demo", () => );
diff --git a/tests/useCookie.test.tsx b/tests/useCookie.test.tsx
new file mode 100644
index 00000000..cc72dc46
--- /dev/null
+++ b/tests/useCookie.test.tsx
@@ -0,0 +1,68 @@
+import { renderHook, act } from '@testing-library/react-hooks';
+import Cookies from 'js-cookie';
+import { useCookie } from '../src';
+
+const setup = (cookieName: string) => renderHook(() => useCookie(cookieName));
+
+it('should have initial value of null if no cookie exists', () => {
+ const { result } = setup('some-cookie');
+
+ expect(result.current[0]).toBeNull();
+});
+
+it('should have initial value of the cookie if it exists', () => {
+ const cookieName = 'some-cookie';
+ const value = 'some-value';
+ Cookies.set(cookieName, value);
+
+ const { result } = setup(cookieName);
+
+ expect(result.current[0]).toBe(value);
+
+ // cleanup
+ Cookies.remove(cookieName);
+});
+
+it('should update the cookie on call to updateCookie', () => {
+ const spy = jest.spyOn(Cookies, 'set');
+
+ const cookieName = 'some-cookie';
+ const { result } = setup(cookieName);
+
+ const newValue = 'some-new-value';
+ act(() => {
+ result.current[1](newValue);
+ });
+
+ expect(result.current[0]).toBe(newValue);
+ expect(spy).toHaveBeenCalledTimes(1);
+ expect(spy).toHaveBeenCalledWith(cookieName, newValue, undefined);
+
+ // cleanup
+ spy.mockRestore();
+ Cookies.remove(cookieName);
+});
+
+it('should delete the cookie on call to deleteCookie', () => {
+ const cookieName = 'some-cookie';
+ const value = 'some-value';
+ Cookies.set(cookieName, value);
+
+ const spy = jest.spyOn(Cookies, 'remove');
+
+ const { result } = setup(cookieName);
+
+ expect(result.current[0]).toBe(value);
+
+ act(() => {
+ result.current[2]();
+ });
+
+ expect(result.current[0]).toBeNull();
+ expect(spy).toHaveBeenCalledTimes(1);
+ expect(spy).toHaveBeenLastCalledWith(cookieName);
+
+ // cleanup
+ spy.mockRestore();
+ Cookies.remove(cookieName);
+});
diff --git a/yarn.lock b/yarn.lock
index f6f27cf2..243c9796 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2929,6 +2929,11 @@
dependencies:
jest-diff "^24.3.0"
+"@types/js-cookie@^2.2.4":
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.4.tgz#f79720b4755aa197c2e15e982e2f438f5748e348"
+ integrity sha512-WTfSE1Eauak/Nrg6cA9FgPTFvVawejsai6zXoq0QYTQ3mxONeRtGhKxa7wMlUzWWmzrmTeV+rwLjHgsCntdrsA==
+
"@types/lolex@^2.1.3":
version "2.1.3"
resolved "https://registry.yarnpkg.com/@types/lolex/-/lolex-2.1.3.tgz#793557c9b8ad319b4c8e4c6548b90893f4aa5f69"
@@ -8787,6 +8792,11 @@ jest@24.9.0:
import-local "^2.0.0"
jest-cli "^24.9.0"
+js-cookie@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
+ integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
+
js-levenshtein@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"