Hone logo
Hone
Problems

Implementing a Custom useHistory Hook in React with TypeScript

The useHistory hook, originally part of React Router, provides a way to programmatically navigate between different routes within a React application. While React Router is a powerful tool, understanding how to implement core functionalities like useHistory can deepen your understanding of React's internals and provide a valuable exercise in custom hook development. This challenge asks you to recreate a simplified version of the useHistory hook using TypeScript.

Problem Description

Your task is to implement a custom useHistory hook in React that mimics the core functionality of the original useHistory hook from React Router. The hook should maintain an internal history stack and provide methods for navigating forward and backward within that stack. The hook should also allow for pushing new entries onto the history stack.

What needs to be achieved:

  • Create a custom hook called useHistory.
  • The hook should initialize an internal history stack (an array) with the current URL (which you can assume is initially "/").
  • The hook should expose the following functions:
    • push(path: string): Adds a new entry to the history stack. The new entry should replace the current location.
    • go(delta: number): Navigates forward or backward in the history stack by a specified number of steps. Positive delta moves forward, negative moves backward.
    • location: A read-only property that returns the current URL (the last element in the history stack).

Key Requirements:

  • The hook must be written in TypeScript.
  • The history stack should be managed internally within the hook.
  • The push function should update the history stack.
  • The go function should handle navigation within the history stack, ensuring that it doesn't go beyond the bounds of the stack.
  • The location property should always return the current URL.

Expected Behavior:

  • When the hook is first called, the history stack should be initialized with /.
  • Calling push('/about') should update the history stack to ['/', '/about'].
  • Calling go(1) after push('/about') should return the current location as /about.
  • Calling go(-1) after push('/about') and go(1) should return the current location as /.
  • Calling go(1) when the history stack only contains / should have no effect.

Edge Cases to Consider:

  • What happens when go() is called with a delta value that would move beyond the beginning or end of the history stack? (Should not throw an error, just do nothing).
  • What happens when push() is called multiple times in a row?
  • How should the hook handle an empty history stack?

Examples

Example 1:

Input: Initial call to useHistory()
Output: location: "/"
Explanation: The history stack is initialized with the current URL, which is assumed to be "/".

Example 2:

Input: push('/about');
Output: location: "/about"
Explanation: The history stack is updated to ['/', '/about']. The location property reflects the new current URL.

Example 3:

Input: push('/about'); go(-1);
Output: location: "/"
Explanation: The history stack is ['/', '/about']. go(-1) navigates back to the previous entry, which is '/'.

Example 4:

Input: push('/about'); push('/contact'); go(1); go(-2);
Output: location: "/"
Explanation: The history stack becomes ['/', '/about', '/contact']. go(1) moves to '/contact'. go(-2) moves back two steps to '/'.

Constraints

  • The hook should be implemented as a functional component using React's useState hook.
  • The push function should accept a string representing the URL path.
  • The go function should accept a number representing the navigation delta.
  • The solution should be concise and readable.
  • The solution should not rely on external libraries beyond React and TypeScript.

Notes

  • You don't need to implement actual routing or browser history management. This is a simplified simulation of the useHistory hook's core functionality.
  • Focus on correctly managing the internal history stack and providing the expected functions and properties.
  • Consider using the useRef hook to persist the history stack across re-renders if needed, although useState is generally sufficient for this simplified scenario.
  • Think about how to ensure that the location property always reflects the current URL in the history stack.
Loading editor...
typescript