Custom React Hooks: UseCounter and UseLocalStorage
This challenge focuses on building custom React hooks to encapsulate common logic and improve code reusability. You'll implement useCounter for managing a counter with increment, decrement, and reset functionalities, and useLocalStorage for persisting a value in local storage. These hooks demonstrate how to abstract state management and side effects, making your components cleaner and more maintainable.
Problem Description
Your task is to create two custom React hooks: useCounter and useLocalStorage.
useCounter Hook:
This hook should manage a counter value and provide functions to interact with it. It should accept an initial value as an argument. The hook should return:
count: The current counter value (number).increment: A function to increment the counter by 1.decrement: A function to decrement the counter by 1.reset: A function to reset the counter to its initial value.
useLocalStorage Hook:
This hook should persist a value in local storage. It should accept a key and an initial value as arguments. The hook should:
- Retrieve the value from local storage using the provided key on initial render. If the key doesn't exist, use the initial value.
- Store the value in local storage whenever it changes.
- Return:
value: The current value (any type).setValue: A function to update the value, which should also update local storage.
Key Requirements:
- Both hooks must be written in TypeScript.
useLocalStoragemust handle potential errors during local storage access gracefully (e.g., browser security settings preventing access). If an error occurs, use the initial value.- The
useLocalStoragehook should only update local storage when the value actually changes. - The hooks should be reusable and independent of any specific component.
Expected Behavior:
useCountershould correctly increment, decrement, and reset the counter value.useLocalStorageshould correctly retrieve and persist values in local storage.- Changes to the value managed by
useLocalStorageshould be reflected in local storage. - The hooks should not cause unnecessary re-renders.
Examples
Example 1: useCounter
Input: Initial value = 0
Output:
- count: 0
- increment() increments count by 1
- decrement() decrements count by 1
- reset() sets count back to 0
Explanation: The hook initializes the counter to 0. Calling increment() increases the counter, decrement() decreases it, and reset() returns it to 0.
Example 2: useLocalStorage
Input: key = "myValue", initialValue = "hello"
Output:
- value: (If "myValue" exists in local storage, its value; otherwise, "hello")
- setValue(newValue): Updates the value and persists it in local storage under the key "myValue".
Explanation: The hook retrieves the value associated with "myValue" from local storage. If it doesn't exist, it uses "hello". setValue updates both the component's state and local storage.
Example 3: useLocalStorage - Error Handling
Input: key = "myValue", initialValue = 123, Local Storage is inaccessible (e.g., due to browser settings)
Output:
- value: 123
- setValue(newValue): Updates the value and attempts to persist it in local storage. If persistence fails, the value is still updated in the component's state, but not in local storage.
Explanation: Because local storage is inaccessible, the hook uses the initial value and continues to function, updating the component's state even if it can't persist to local storage.
Constraints
- The
useCounterhook should not have any performance constraints beyond standard React best practices. - The
useLocalStoragehook should avoid unnecessary local storage writes. Only write to local storage when the value actually changes. - Both hooks must be implemented using functional components and React hooks.
- The code must be valid TypeScript.
Notes
- Consider using
useStatefor managing the internal state within each hook. - For
useLocalStorage, uselocalStorage.getItem()andlocalStorage.setItem()to interact with local storage. - Remember to handle potential errors when accessing local storage.
- Think about how to prevent unnecessary re-renders when the value changes. Use memoization techniques if needed.
- Focus on creating clean, reusable, and well-documented code.
- The goal is to demonstrate your understanding of custom hooks and their benefits.