Skip to content

Commit

Permalink
Merge pull request #21 from mcrowder65/add-local-storage
Browse files Browse the repository at this point in the history
added useLocalStorage
  • Loading branch information
mcrowder65 committed Mar 23, 2019
2 parents d64f1a9 + a7a5a17 commit 8b3a9f4
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 1 deletion.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@
Reusable javascript utilities that serve [Matt Crowder!](https://twitter.com/mcrowder65)


### Usage in tests

Under the use-local-storage-set-state module is obviously using localStorage.
When using this with jest, you will run into errors since jest does not have localStorage.
Which is why this package requires `jest-localstorage-mock` as a peerDependency.

In order to get your tests to work, run `npm install -D jest-localstorage-mock`, and then in your jest configuration, add:

```json
"setupFiles": [
"jest-localstorage-mock"
]
```

#### How to publish to npm
While on the master branch, run `npm version $versionType`
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"src/**/*.js",
"!src/index.js"
],
"setupFiles": [
"jest-localstorage-mock"
],
"setupFilesAfterEnv": [
"<rootDir>/test/config.js"
],
Expand All @@ -42,6 +45,7 @@
}
},
"peerDependencies": {
"jest-localstorage-mock": "2.4.0",
"@material-ui/core": "3.9.2",
"react": "16.8.4",
"react-dom": "16.8.4"
Expand All @@ -59,9 +63,13 @@
"eslint-config-mcrowder65": "0.0.46",
"jest": "24.4.0",
"jest-dom": "3.0.1",
"jest-localstorage-mock": "2.4.0",
"react": "16.8.4",
"react-dom": "16.8.4",
"react-testing-library": "6.0.0"
},
"babelConfig": ".babelrc.js"
"babelConfig": ".babelrc.js",
"dependencies": {
"store": "2.0.12"
}
}
121 changes: 121 additions & 0 deletions src/__tests__/useLocalStorageSetState.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React from "react";
import PropTypes from "prop-types";
import { render, fireEvent } from "react-testing-library";

import useLocalStorageSetState from "../useLocalStorageSetState";

function MyComponent() {
const [name, setName] = useLocalStorageSetState("", "name");
const [flag, setFlag] = useLocalStorageSetState(false, "flag");
const [array, setArray] = useLocalStorageSetState([], "array");
return (
<div>
{name}
<input
data-testid="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>

<button data-testid="button" onClick={() => setFlag((state) => !state)}>
{String(flag)}
</button>
{array.join(",")}
<button
data-testid="array-button"
onClick={() => setArray((state) => [...state, state.length + 1])}
/>
</div>
);
}

test("that after inserting text into the document then remounting, the text will remain", () => {
const { queryByText, queryByTestId, unmount, rerender } = render(
<MyComponent />,
);

const newValue = "Lebron James";
fireEvent.change(queryByTestId("name"), { target: { value: newValue } });

expect(queryByText(newValue)).toBeInTheDocument();

unmount();

rerender(<MyComponent />);

expect(queryByText(newValue)).toBeInTheDocument();
});

test("that on component reloads, booleans persist", () => {
const { getByText, getByTestId, unmount, rerender } = render(<MyComponent />);

fireEvent.click(getByTestId("button"));

expect(getByText(/true/i)).toBeInTheDocument();

unmount();

rerender(<MyComponent />);

expect(getByText(/true/i)).toBeInTheDocument();
});

test("that on component reloads, arrays persist", () => {
const { getByText, getByTestId, unmount, rerender } = render(<MyComponent />);

fireEvent.click(getByTestId("array-button"));

expect(getByText(/1/i)).toBeInTheDocument();

fireEvent.click(getByTestId("array-button"));

expect(getByText(/1,2/i)).toBeInTheDocument();

unmount();

rerender(<MyComponent />);

expect(getByText(/1,2/i)).toBeInTheDocument();
});

test("that it throws when no name is provided", () => {
expect(() => useLocalStorageSetState("asdf")).toThrow();
});

test("that when passing a function, it utilizes it to set the value", () => {
function MyComp(props) {
const [index, setIndex] = useLocalStorageSetState((prev) => {
if (prev >= props.arr.length || prev === undefined) {
return 0;
} else {
return prev;
}
}, "index-local-storage");
return (
<div>
<button data-testid="set-index-0" onClick={() => setIndex(0)} />
<div data-testid="index">{props.arr[index]}</div>
<button data-testid="set-index-1" onClick={() => setIndex(1)} />
</div>
);
}

MyComp.propTypes = {
arr: PropTypes.array.isRequired,
};

const { getByTestId, getByText, rerender, unmount } = render(
<MyComp arr={["first", "second"]} />,
);

expect(getByText(/first/i)).toBeInTheDocument();

fireEvent.click(getByTestId("set-index-1"));

expect(getByText(/second/i)).toBeInTheDocument();

unmount();
rerender(<MyComp arr={["first"]} />);

expect(getByText(/first/i)).toBeInTheDocument();
});
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import LoaderButton from "./LoaderButton";
import LoaderCard from "./LoaderCard";
import Login from "./Login";
import Signup from "./Signup";
import useLocalStorageSetState from "./useLocalStorageSetState";

export {
useArrowKeyListener,
Expand All @@ -22,4 +23,5 @@ export {
LoaderCard,
Login,
Signup,
useLocalStorageSetState,
};
30 changes: 30 additions & 0 deletions src/useLocalStorageSetState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import store from "store";
function useLocalStorageSetState(initialValue, name) {
if (!name) {
throw new Error(
"Name must be provided to persist properly to localStorage",
);
}
let actualInitialValue =
store.get(name) !== undefined ? store.get(name) : initialValue;
if (typeof initialValue === "function") {
actualInitialValue = initialValue(actualInitialValue);
}
const [value, setValue] = React.useState(actualInitialValue);

const theirSetValue = (theirNewValue) => {
let valueToSet;
if (typeof theirNewValue === "function") {
valueToSet = theirNewValue(value);
setValue(valueToSet);
} else {
setValue(theirNewValue);
valueToSet = theirNewValue;
}
store.set(name, valueToSet);
};
return [value, theirSetValue];
}

export default useLocalStorageSetState;

0 comments on commit 8b3a9f4

Please sign in to comment.