Skip to content

Commit 233f375

Browse files
Abdulnasır OlcanAbdulnasır Olcan
authored andcommitted
🚀 useLocalStorage hooks and test added
1 parent a290d1f commit 233f375

File tree

8 files changed

+209
-4
lines changed

8 files changed

+209
-4
lines changed

example/vue3/src/App.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<template>
22
<nav>
3-
<router-link class="router-list" to="/" active-class="active"
4-
>useStringCase</router-link
3+
<router-link class="router-list" to="/" active-class="active">useStringCase</router-link>
4+
<router-link class="router-list" to="/use-local-storage-hook" active-class="active"
5+
>useLocalStorage</router-link
56
>
67
<router-link class="router-list" to="/use-state-hook" active-class="active"
78
>useState</router-link
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<script setup lang="ts">
2+
import { useLocalStorage } from 'vue3-use-hooks';
3+
const { value, remove } = useLocalStorage('test', 1);
4+
</script>
5+
6+
<template>
7+
<div>
8+
<p><b>Value is: </b> {{ value }}</p>
9+
<button class="fourth" @click="value = '2'">Change</button>
10+
<button class="first" @click="remove">Remove</button>
11+
</div>
12+
</template>
13+
14+
<style lang="scss" scoped>
15+
$green: #2ecc71;
16+
$red: #e74c3c;
17+
$blue: #3498db;
18+
$yellow: #f1c40f;
19+
$purple: #8e44ad;
20+
$turquoise: #1abc9c;
21+
22+
p {
23+
background-color: #f4f4f4;
24+
border: 1px solid #ddd;
25+
border-left: 0.3rem solid #42b983;
26+
color: #666;
27+
font-family: monospace;
28+
padding: 1em 1.5em;
29+
display: block;
30+
}
31+
32+
button {
33+
box-sizing: border-box;
34+
appearance: none;
35+
background-color: transparent;
36+
border: 2px solid $red;
37+
border-radius: 0.6em;
38+
color: $red;
39+
cursor: pointer;
40+
// display: flex;
41+
align-self: center;
42+
line-height: 1;
43+
margin: 10px;
44+
padding: 1.2em 1.2em;
45+
text-decoration: none;
46+
text-align: center;
47+
text-transform: uppercase;
48+
font-family: 'Montserrat', sans-serif;
49+
font-weight: 700;
50+
font-size: 12px;
51+
52+
&:hover,
53+
&:focus {
54+
outline: 0;
55+
}
56+
}
57+
58+
.fourth {
59+
border-color: $yellow;
60+
color: #000;
61+
background: {
62+
image: linear-gradient(45deg, $yellow 50%, transparent 50%);
63+
position: 100%;
64+
size: 400%;
65+
}
66+
transition: background 300ms ease-in-out;
67+
68+
&:hover {
69+
color: #fff;
70+
background-position: 0;
71+
}
72+
}
73+
</style>
74+

example/vue3/src/router/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ import { createRouter, createWebHistory } from 'vue-router';
33
const useModalComponent = () => import('../components/useModal.vue');
44
const useStateComponent = () => import('../components/useState.vue');
55
const useStringCaseComponent = () => import('../components/useStringCase.vue');
6+
const useLocalStorageComponent = () => import('../components/useLocalStorage.vue');
67

78
const routes = [
89
{
910
path: '/',
1011
name: 'useStringCase',
1112
component: useStringCaseComponent
1213
},
14+
{
15+
path: '/use-local-storage-hook',
16+
name: 'useState',
17+
component: useLocalStorageComponent
18+
},
1319
{
1420
path: '/use-state-hook',
1521
name: 'useState',

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vue3-use-hooks",
3-
"version": "0.0.5",
3+
"version": "0.0.6",
44
"description": "vue3 use hooks",
55
"author": "Abdulnasır OLCAN",
66
"license": "MIT",

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './useState';
22
export { default as useStringCase } from './useStringCase';
3-
export { default as useModal } from './useModal';
3+
export { default as useModal } from './useModal';
4+
export { default as useLocalStorage } from './useLocalStorage';

src/useLocalStorage.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { ref, onMounted, onUnmounted, watchEffect } from 'vue';
2+
3+
const useLocalStorage = (storageKey: any, defaultValue = '') => {
4+
const value = ref(null);
5+
const init = () => {
6+
const item = localStorage.getItem(storageKey);
7+
if (item !== null) {
8+
value.value = parseItem(item);
9+
return;
10+
}
11+
/* @ts-ignore*/
12+
value.value = defaultValue;
13+
};
14+
15+
const parseItem = (item: any) => {
16+
let value = null;
17+
try {
18+
value = JSON.parse(item);
19+
} catch {
20+
value = item;
21+
}
22+
23+
return value;
24+
};
25+
26+
const handler = (event: any) => {
27+
if (event.key === storageKey) {
28+
value.value = event.newValue ? parseItem(event.newValue) : null;
29+
}
30+
};
31+
32+
let initialized = false;
33+
34+
if (typeof window !== 'undefined') {
35+
init();
36+
initialized = true;
37+
}
38+
39+
onMounted(() => {
40+
if (!initialized) {
41+
init();
42+
}
43+
44+
window.addEventListener('storage', handler, true);
45+
});
46+
47+
watchEffect(() => {
48+
if (value.value) {
49+
localStorage.setItem(storageKey, JSON.stringify(value.value));
50+
}
51+
});
52+
53+
onUnmounted(() => {
54+
window.removeEventListener('storage', handler);
55+
});
56+
57+
const remove = () => {
58+
try {
59+
localStorage.removeItem(storageKey);
60+
value.value = null;
61+
} catch (error) {
62+
// localStorage can throw.
63+
console.error(error);
64+
}
65+
};
66+
67+
return {
68+
value,
69+
remove
70+
};
71+
};
72+
73+
export default useLocalStorage;

tests/useLocalStorage.test.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { mount } from '@vue/test-utils';
2+
import { mountComposition } from './utils';
3+
import useLocalStorage from '../src/useLocalStorage';
4+
import { nextTick } from 'vue';
5+
6+
const mock = {
7+
key: 'test',
8+
value: 'value'
9+
};
10+
11+
beforeEach(() => {
12+
window.localStorage.clear();
13+
});
14+
15+
describe('useLocalStorage', () => {
16+
it('should be defined useLocalStorage', () => {
17+
expect(useLocalStorage).toBeDefined();
18+
});
19+
20+
it('should get localStorage value', () => {
21+
window.localStorage.setItem(mock.key, mock.value);
22+
const wrapper = mount(
23+
mountComposition(() => {
24+
const { value } = useLocalStorage(mock.key);
25+
return { value };
26+
})
27+
);
28+
nextTick();
29+
expect(wrapper.vm.value).toEqual(mock.value);
30+
});
31+
32+
it('should set localStorage value', () => {
33+
const wrapper = mount(
34+
mountComposition(() => {
35+
const { value } = useLocalStorage(mock.key, mock.value);
36+
return { value };
37+
})
38+
);
39+
nextTick();
40+
expect(wrapper.vm.value).toEqual(mock.value);
41+
});
42+
});

tests/utils/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const mountComposition = (callback, options = {}) => ({
2+
template: '<div></div>',
3+
...options.component,
4+
setup() {
5+
return callback();
6+
},
7+
});
8+

0 commit comments

Comments
 (0)