-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🚀 Implement useLocalStorage exercise (#104)
Co-authored-by: Rohan Gupta <rohan.gupta@gameskraft.com>
- Loading branch information
1 parent
0a2f64f
commit a7da268
Showing
12 changed files
with
255 additions
and
1,091 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# useLocalStorage | ||
|
||
_Tag_: **React Hooks** | ||
|
||
Write a `useLocalStorage` custom hook that takes in a required `key` as a string, and an optional `initialValue`. | ||
|
||
Calling `useLocalStorage` in a component should save the `initialValue` in localStorage at the given `key` when the component first mounts. If a value already exists at that `key`, the `initialValue` parameter should be ignored. | ||
|
||
The `useLocalStorage` function should return an array with the current value as the first element and a setter function as the second element. The setter function should take in a new value as a parameter and update localStorage at the original `key`. | ||
|
||
When the setter function is called, the component should re-render, just as it would when a standard piece of state is updated. | ||
|
||
Any value added to localStorage should first be passed to `JSON.stringify`. When reading the value from localStorage, `JSON.parse` should be used to parse the original value. | ||
|
||
For simplicity, you can asssume the `key` parameter will not change between renders. | ||
|
||
### Sample Usage | ||
|
||
```jsx | ||
function SaveValues() { | ||
const [value, setValue] = useLocalStorage('name', ''); | ||
return <input value={value} onChange={(e) => setValue(e.target.value)} />; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
export const sampleUsage = `function SaveValues() { | ||
const [value, setValue] = useLocalStorage('name', ''); | ||
return <input value={value} onChange={(e) => setValue(e.target.value)} />; | ||
}`; | ||
|
||
export const solutionCode = `// useLocalStorage hook | ||
import { useCallback, useEffect, useState } from 'react'; | ||
const useLocalStorage = (key: string, initialValue?: any) => { | ||
const [value, setValue] = useState<any>(initialValue); | ||
const setValueInStorage = useCallback((valueToSet: any) => { | ||
const stringifiedValue = JSON.stringify(valueToSet); | ||
localStorage.setItem(key, stringifiedValue); | ||
setValue(valueToSet); | ||
}, [key]); | ||
useEffect(() => { | ||
const parsedValue = JSON.parse(localStorage.getItem(key) ?? 'null'); | ||
if (!parsedValue) { | ||
localStorage.setItem(key, JSON.stringify(initialValue)); | ||
} else { | ||
setValue(parsedValue); | ||
} | ||
}, [key, initialValue]); | ||
return [value, setValueInStorage]; | ||
}; | ||
export default useLocalStorage; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import React from 'react'; | ||
import classes from './styles.module.scss'; | ||
import ProjectPage from '../ProjectPage'; | ||
import QuestionDetails from '../QuestionDetails'; | ||
import { PROJECT_COLORS } from '../../constants/theme'; | ||
import CodeBlock from '../UI/CodeBlock'; | ||
import { sampleUsage, solutionCode } from './codeBlock'; | ||
|
||
const UseLocalStorage: React.FC<Props> = () => { | ||
return ( | ||
<ProjectPage background={PROJECT_COLORS.PROJECT38.background} containerClassName={classes.pageContainer}> | ||
<QuestionDetails | ||
title='Use Local Storage' | ||
titleClassName={classes.questionTitle} | ||
containerClassName={classes.questionDetails} | ||
descriptionClassName={classes.questionDescription}> | ||
<p> | ||
Write a <span className={classes.questionDescriptionHighlight}>useLocalStorage</span> custom hook that takes | ||
in a required <span className={classes.questionDescriptionHighlight}>key</span> as a string, and an optional{' '} | ||
<span className={classes.questionDescriptionHighlight}>initialValue</span>. | ||
</p> | ||
<p> | ||
Calling <span className={classes.questionDescriptionHighlight}>useLocalStorage</span> in a component should | ||
save the <span className={classes.questionDescriptionHighlight}>initialValue</span> in localStorage at the | ||
given <span className={classes.questionDescriptionHighlight}>key</span> when the component first mounts. If a | ||
value already exists at that <span className={classes.questionDescriptionHighlight}>key</span>, the{' '} | ||
<span className={classes.questionDescriptionHighlight}>initialValue</span> parameter should be ignored. | ||
</p> | ||
<p> | ||
The <span className={classes.questionDescriptionHighlight}>useLocalStorage</span> function should return an | ||
array with the current value as the first element and a setter function as the second element. The setter | ||
function should take in a new value as a parameter and update localStorage at the original{' '} | ||
<span className={classes.questionDescriptionHighlight}>key</span>. | ||
</p> | ||
<p> | ||
When the setter function is called, the component should re-render, just as it would when a standard piece of | ||
state is updated. | ||
</p> | ||
<p> | ||
Any value added to localStorage should first be passed to{' '} | ||
<span className={classes.questionDescriptionHighlight}>JSON.stringify</span>. When reading the value from | ||
localStorage, <span className={classes.questionDescriptionHighlight}>JSON.parse</span> should be used to parse | ||
the original value. | ||
</p> | ||
<p> | ||
For simplicity, you can asssume the <span className={classes.questionDescriptionHighlight}>key</span>{' '} | ||
parameter will not change between renders. | ||
</p> | ||
<div className={classes.codeWrapper}> | ||
<h3 className={classes.codeBlockHeader}>Sample Usage</h3> | ||
<CodeBlock codeString={sampleUsage} /> | ||
</div> | ||
</QuestionDetails> | ||
<section className={classes.solutionContainer}> | ||
<CodeBlock codeString={solutionCode} containerClassName={classes.fullHeight} hideCode /> | ||
</section> | ||
</ProjectPage> | ||
); | ||
}; | ||
|
||
export default UseLocalStorage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
@import '../../constants/theme.scss'; | ||
@import '../../utils/mixins.scss'; | ||
|
||
.pageContainer { | ||
@media screen and (min-width: 992px) { | ||
overflow-y: hidden; | ||
} | ||
} | ||
|
||
.questionDetails { | ||
@media screen and (min-width: 992px) { | ||
max-width: 40rem; | ||
max-height: 85vh; | ||
overflow-y: auto; | ||
} | ||
} | ||
|
||
.questionDescription { | ||
@include spacing (0 0.25rem); | ||
word-break: break-word; | ||
white-space: break-spaces; | ||
} | ||
|
||
.questionTitle { | ||
color: $project-header-38; | ||
} | ||
|
||
.break { | ||
@include spacing ($margin: 0.5rem 0); | ||
} | ||
|
||
.questionDescriptionHighlight { | ||
@include spacing (0 0.25rem); | ||
background-color: $project-highlight-38; | ||
border-radius: 0.25rem; | ||
} | ||
|
||
.codeBlock { | ||
@include spacing (0.5rem 0.75rem); | ||
overflow-x: auto; | ||
border-radius: 0.5rem; | ||
|
||
@media screen and (min-width: 992px) { | ||
@include spacing (0.75rem 1rem); | ||
} | ||
} | ||
|
||
.codeWrapper { | ||
@include spacing (0.5rem 0); | ||
} | ||
|
||
.codeBlockHeader { | ||
@include spacing (0.5rem 0); | ||
color: $white; | ||
} | ||
|
||
.solutionContainer { | ||
@include spacing (1rem); | ||
border: 1px dotted rgba($color: $white, $alpha: 0.5); | ||
border-radius: 0.25rem; | ||
align-self: stretch; | ||
flex: 1; | ||
|
||
@media screen and (min-width: 992px) { | ||
overflow-y: auto; | ||
max-width: 40rem; | ||
max-height: 85vh; | ||
} | ||
} | ||
|
||
.fullHeight { | ||
@include spacing (0, 0); | ||
height: 100%; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { useCallback, useEffect, useState } from 'react'; | ||
|
||
const useLocalStorage = (key: string, initialValue?: any) => { | ||
const [value, setValue] = useState<any>(initialValue); | ||
|
||
const setValueInStorage = useCallback((valueToSet: any) => { | ||
const stringifiedValue = JSON.stringify(valueToSet); | ||
localStorage.setItem(key, stringifiedValue); | ||
setValue(valueToSet); | ||
}, [key]); | ||
|
||
useEffect(() => { | ||
const parsedValue = JSON.parse(localStorage.getItem(key) ?? 'null'); | ||
if (!parsedValue) { | ||
localStorage.setItem(key, JSON.stringify(initialValue)); | ||
} else { | ||
setValue(parsedValue); | ||
} | ||
}, [key, initialValue]); | ||
|
||
return [value, setValueInStorage]; | ||
}; | ||
|
||
export default useLocalStorage; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { LOREM_IPSUM } from "../constants"; | ||
import { LOREM_IPSUM } from '../constants'; | ||
|
||
/** | ||
* | ||
|