1. 首页

通过简单的示例来理解React Hook

useTheme

This hook makes it easy to dynamically change the appearance of your app using CSS variables. You simply pass in an object containing key/value pairs of the CSS variables you’d like to update and the hook updates each variable in the document’s root element. This is useful in situations where you can’t define styles inline (no psudeoclass support) and there are too many style permutations to include each theme in your stylesheet (such as a web app that lets users customize the look of their profile). It’s worth noting that many css-in-js libraries support dynamic styles out of the box, but it’s interesting to experiment with how this can be done with just CSS variables and a React Hook. The example below is intentionally very simple, but you could imagine the theme object being stored in state or fetched from an API. Be sure to check out the CodeSandbox demo for a more interesting example and to see the accompanying stylesheet.

import { useLayoutEffect } from 'react';
import './styles.scss'; // -> https://codesandbox.io/s/15mko9187

// Usage
const theme = {
  'button-padding': '16px',
  'button-font-size': '14px',
  'button-border-radius': '4px',
  'button-border': 'none',
  'button-color': '#FFF',
  'button-background': '#6772e5',
  'button-hover-border': 'none',
  'button-hover-color': '#FFF'
};

function App() {
  useTheme(theme);

  return (
    <div>
      <button className="button">Button</button>
    </div>
  );
}

// Hook
function useTheme(theme) {
  useLayoutEffect(
    () => {
      // Iterate through each value in theme object
      for (var key in theme) {
        // Update css variables in document's root element
        document.documentElement.style.setProperty(`--${key}`, theme[key]);
      }
    },
    [theme] // Only call again if theme object reference changes
  );
}

```javascript 

Also check out:

+   [CSS Variables and React][3] - The blog post by Dan Bahrami that inspired this recipe.

January 07, 2019•[Open in CodeSandbox][4]•[Suggest a change][5]

## [useSpring][6]

This hook is part of the [react-spring][7] animation library which allows for highly performant physics-based animations. I try to avoid including dependencies in these recipes, but once in awhile I'm going to make an exception for hooks that expose the functionality of **really** useful libraries. One nice thing about react-spring is that it allows you to completely skip the React render cycle when applying animations, often giving a pretty substantial performance boost. In our recipe below we render a row of cards and apply a springy animation effect related to the mouse position over any given card. To make this work we call the useSpring hook with an array of values we want to animate, render an animated.div component (exported by react-spring), get the mouse position over a card with the onMouseMove event, then call setAnimatedProps (function returned by the hook) to update that set of values based on the mouse position. Read through the comments in the recipe below for more details or jump right over to the [CodeSandbox demo][8]. I liked this effect so much I ended up using it on my [startup's landing page][9] 😎


import { useState, useRef } from ‘react’;
import { useSpring, animated } from ‘react-spring’;

// Displays a row of cards
// Usage of hook is within component below
function App() {
return (

{cards.map((card, i) => (

{card.title}
{card.description}


))}

);
}

function Card({ children }) {
// We add this ref to card element and use in onMouseMove event …
// … to get element’s offset and dimensions.
const ref = useRef();

// Keep track of whether card is hovered so we can increment …
// … zIndex to ensure it shows up above other cards when animation causes overlap.
const [isHovered, setHovered] = useState(false);

// The useSpring hook
const [animatedProps, setAnimatedProps] = useSpring({
// Array containing [rotateX, rotateY, and scale] values.
// We store under a single key (xys) instead of separate keys …
// … so that we can use animatedProps.xys.interpolate() to …
// … easily generate the css transform value below.
xys: [0, 0, 1],
// Setup physics
config: { mass: 10, tension: 400, friction: 40, precision: 0.00001 }
});

return (
<animated.div
ref={ref}
className=”card”
onMouseEnter={() => setHovered(true)}
onMouseMove={({ clientX, clientY }) => {
// Get mouse x position within card
const x =
clientX –
(ref.current.offsetLeft –
(window.scrollX || window.pageXOffset || document.body.scrollLeft));

    // Get mouse y position within card
    const y =
      clientY -
      (ref.current.offsetTop -
        (window.scrollY || window.pageYOffset || document.body.scrollTop));

    // Set animated values based on mouse position and card dimensions
    const dampen = 50; // Lower the number the less rotation
    const xys = [
      -(y - ref.current.clientHeight / 2) / dampen, // rotateX
      (x - ref.current.clientWidth / 2) / dampen, // rotateY
      1.07 // Scale
    ];

    // Update values to animate to
    setAnimatedProps({ xys: xys });
  }}
  onMouseLeave={() => {
    setHovered(false);
    // Set xys back to original
    setAnimatedProps({ xys: [0, 0, 1] });
  }}
  style={{
    // If hovered we want it to overlap other cards when it scales up
    zIndex: isHovered ? 2 : 1,
    // Interpolate function to handle css changes
    transform: animatedProps.xys.interpolate(
      (x, y, s) =>
        `perspective(600px) rotateX(${x}deg) rotateY(${y}deg) scale(${s})`
    )
  }}
>
  {children}
</animated.div>

);
}


Also check out: + [react-spring][10] - Offical docs with lots of fun animation examples. See section about the [useSpring hook here][11]. + [Card Demo][12] - Original useSpring demo that my code is based on by [0xca0a][13]. + [Scroll Animation Demo][14] - Another useSpring demo that animates on scroll by [0xca0a][15]. + [useAnimation][16] - Animation hook recipe that I previously posted with no dependencies. Won't be as performant and is time-based rather than physics-based. November 21, 2018•[Open in CodeSandbox][17]•[Suggest a change][18] ## [useHistory][19] This hook makes it really easy to add undo/redo functionality to your app. Our recipe is a simple drawing app. It generates a grid of blocks, allows you to click any block to toggle its color, and uses the useHistory hook so we can undo, redo, or clear all changes to the canvas. Check out our [CodeSandbox demo][20]. Within our hook we're using useReducer to store state instead of useState, which should look familiar to anyone that's used redux (read more about useReducer in the [official docs][21]). The hook code was copied, with minor changes, from the excellent [use-undo library][22], so if you'd like to pull this into your project you can also use that library via npm.

import { useReducer, useCallback } from ‘react’;

// Usage
function App() {
const { state, set, undo, redo, clear, canUndo, canRedo } = useHistory({});

return (

<

div className=”container”>

👩‍🎨 Click squares to draw



  <div className="grid">
    {((blocks, i, len) => {
      // Generate a grid of blocks
      while (++i <= len) {
        const index = i;
        blocks.push(
          <div
            // Give block "active" class if true in state object
            className={'block' + (state[index] ? ' active' : '')}
            // Toggle boolean value of clicked block and merge into current state
            onClick={() => set({ ...state, [index]: !state[index] })}
            key={i}
          />
        );
      }
      return blocks;
    })([], 0, 625)}
  </div>
</div>

);
}

// Initial state that we pass into useReducer
const initialState = {
// Array of previous state values updated each time we push a new state
past: [],
// Current state value
present: null,
// Will contain “future” state values if we undo (so we can redo)
future: []
};

// Our reducer function to handle state changes based on action
const reducer = (state, action) => {
const { past, present, future } = state;

switch (action.type) {
case ‘UNDO’:
const previous = past[past.length – 1];
const newPast = past.slice(0, past.length – 1);

  return {
    past: newPast,
    present: previous,
    future: [present, ...future]
  };
case 'REDO':
  const next = future[0];
  const newFuture = future.slice(1);

  return {
    past: [...past, present],
    present: next,
    future: newFuture
  };
case 'SET':
  const { newPresent } = action;

  if (newPresent === present) {
    return state;
  }
  return {
    past: [...past, present],
    present: newPresent,
    future: []
  };
case 'CLEAR':
  const { initialPresent } = action;

  return {
    ...initialState,
    present: initialPresent
  };

}
};

// Hook
const useHistory = initialPresent => {
const [state, dispatch] = useReducer(reducer, {
…initialState,
present: initialPresent
});

const canUndo = state.past.length !== 0;
const canRedo = state.future.length !== 0;

// Setup our callback functions
// We memoize with useCallback to prevent unecessary re-renders

const undo = useCallback(
() => {
if (canUndo) {
dispatch({ type: ‘UNDO’ });
}
},
[canUndo, dispatch]
);

const redo = useCallback(
() => {
if (canRedo) {
dispatch({ type: ‘REDO’ });
}
},
[canRedo, dispatch]
);

const set = useCallback(newPresent => dispatch({ type: ‘SET’, newPresent }), [
dispatch
]);

const clear = useCallback(() => dispatch({ type: ‘CLEAR’, initialPresent }), [
dispatch
]);

// If needed we could also return past and future state
return { state: state.present, set, undo, redo, clear, canUndo, canRedo };
};


Also check out: + [xxhomey19/use-undo][23] - The library that this code was copied from with minor changes. Also returns previous and future states from hook, but doesn't have a clear action. + [React useHistory hook][24] - An alternate implementation of useHistory by [@juice49][25]. November 19, 2018•[Open in CodeSandbox][26]•[Suggest a change][27] ## [useScript][28] This hook makes it super easy to dynamically load an external script and know when its loaded. This is useful when you need to interact with a 3rd party libary (Stripe, Google Analytics, etc) and you'd prefer to load the script when needed rather then include it in the document head for every page request. In the example below we wait until the script has loaded successfully before calling a function declared in the script. If you're interested in seeing how this would look if implemented as a Higher Order Component then check out the [source of react-script-loader-hoc][29]. I personally find it much more readable as a hook. Another advantage is because it's so easy to call the same hook multiple times to load multiple different scripts, unlike the HOC implementation, we can skip adding support for passing in multiple src strings.

import { useState, useEffect } from ‘react’;

// Usage
function App() {
const [loaded, error] = useScript(
‘https://pm28k14qlj.codesandbox.io/test-external-script.js’
);

return (

Script loaded: {loaded.toString()}

{loaded && !error && (

Script function call response: {TEST_SCRIPT.start()}

)}

);
}

// Hook
let cachedScripts = [];
function useScript(src) {
// Keeping track of script loaded and error state
const [state, setState] = useState({
loaded: false,
error: false
});

useEffect(
() => {
// If cachedScripts array already includes src that means another instance …
// … of this hook already loaded this script, so no need to load again.
if (cachedScripts.includes(src)) {
setState({
loaded: true,
error: false
});
} else {
cachedScripts.push(src);

    // Create script
    let script = document.createElement('script');
    script.src = src;
    script.async = true;

    // Script event listener callbacks for load and error
    const onScriptLoad = () => {
      setState({
        loaded: true,
        error: false
      });
    };

    const onScriptError = () => {
      // Remove from cachedScripts we can try loading again
      const index = cachedScripts.indexOf(src);
      if (index >= 0) cachedScripts.splice(index, 1);
      script.remove();

      setState({
        loaded: true,
        error: true
      });
    };

    script.addEventListener('load', onScriptLoad);
    script.addEventListener('error', onScriptError);

    // Add script to document body
    document.body.appendChild(script);

    // Remove event listeners on cleanup
    return () => {
      script.removeEventListener('load', onScriptLoad);
      script.removeEventListener('error', onScriptError);
    };
  }
},
[src] // Only re-run effect if script src changes

);

return [state.loaded, state.error];
}


Also check out: + [react-script-loader-hoc][30] - HOC implemantion of same logic for the sake of comparison. + [useScript from palmerhq/the-platform][31] - Similar hook but returns a promise for use with React Suspense. November 15, 2018•[Open in CodeSandbox][32]•[Suggest a change][33] ## [useKeyPress][34] This hook makes it easy to detect when the user is pressing a specific key on their keyboard. The recipe is fairly simple, as I want to show how little code is required, but I challenge any readers to create a more advanced version of this hook. Detecting when multiple keys are held down at the same time would be a nice addition. Bonus points: also require they be held down in a specified order. Feel free to share anything you've created in this [recipe's gist][35].

import { useState, useEffect } from ‘react’;

// Usage
function App() {
// Call our hook for each key that we’d like to monitor
const happyPress = useKeyPress(‘h’);
const sadPress = useKeyPress(‘s’);
const robotPress = useKeyPress(‘r’);
const foxPress = useKeyPress(‘f’);

return (

h, s, r, f
{happyPress && ‘😊’}
{sadPress && ‘😢’}
{robotPress && ‘🤖’}
{foxPress && ‘🦊’}

);
}

// Hook
function useKeyPress(targetKey) {
// State for keeping track of whether key is pressed
const [keyPressed, setKeyPressed] = useState(false);

// If pressed key is our target key then set to true
function downHandler({ key }) {
if (key === targetKey) {
setKeyPressed(true);
}
}

// If released key is our target key then set to false
const upHandler = ({ key }) => {
if (key === targetKey) {
setKeyPressed(false);
}
};

// Add event listeners
useEffect(() => {
window.addEventListener(‘keydown’, downHandler);
window.addEventListener(‘keyup’, upHandler);
// Remove event listeners on cleanup
return () => {
window.removeEventListener(‘keydown’, downHandler);
window.removeEventListener(‘keyup’, upHandler);
};
}, []); // Empty array ensures that effect is only run on mount and unmount

return keyPressed;
}


Also check out: + [useMultiKeyPress][36] - A fork of this recipe by [@jhsu][37] that detects multiple keys at once. November 14, 2018•[Open in CodeSandbox][38]•[Suggest a change][39] ## [useMemo][40] React has a built-in hook called useMemo that allows you to memoize expensive functions so that you can avoid calling them on every render. You simple pass in a function and an array of inputs and useMemo will only recompute the memoized value when one of the inputs has changed. In our example below we have an expensive function called computeLetterCount (for demo purposes we make it slow by including a large and completely unnecessary loop). When the current selected word changes you'll notice a delay as it has to recall computeLetterCount on the new word. We also have a separate counter that gets incremented everytime the increment button is clicked. When that counter is incremented you'll notice that there is zero lag between renders. This is because computeLetterCount is not called again. The input word hasn't changed and thus the cached value is returned. You'll probably want to check out the [CodeSandbox demo][41] so you can see for yourself.

import { useState, useMemo } from ‘react’;

// Usage
function App() {
// State for our counter
const [count, setCount] = useState(0);
// State to keep track of current word in array we want to show
const [wordIndex, setWordIndex] = useState(0);

// Words we can flip through and view letter count
const words = [‘hey’, ‘this’, ‘is’, ‘cool’];
const word = words[wordIndex];

// Returns number of letters in a word
// We make it slow by including a large and completely unnecessary loop
const computeLetterCount = word => {
let i = 0;
while (i < 1000000000) i++;
return word.length;
};

// Memoize computeLetterCount so it uses cached return value if input array …
// … values are the same as last time the function was run.
const letterCount = useMemo(() => computeLetterCount(word), [word]);

// This would result in lag when incrementing the counter because …
// … we’d have to wait for expensive function when re-rendering.
//const letterCount = computeLetterCount(word);

return (

Compute number of letters (slow 🐌)

“{word}” has {letterCount} letters

Increment a counter (fast ⚡️)

Counter: {count}

);
}


November 12, 2018•[Open in CodeSandbox][42]•[Suggest a change][43] ## [useDebounce][44] This hook allows you to debounce any fast changing value. The debounced value will only reflect the latest value when the useDebounce hook has not been called for the specified time period. When used in conjuction with useEffect, as we do in the recipe below, you can easily ensure that expensive operations like API calls are not executed too frequently. The example below allows you to search the Marvel Comic API and uses useDebounce to prevent API calls from being fired on every keystroke. Be sure to theck out the [CodeSandbox demo][45] for this one. Hook code and inspiration from [github.com/xnimorz/use…][46].

import { useState, useEffect, useRef } from ‘react’;

// Usage
function App() {
// State and setters for …
// Search term
const [searchTerm, setSearchTerm] = useState(”);
// API search results
const [results, setResults] = useState([]);
// Searching status (whether there is pending API request)
const [isSearching, setIsSearching] = useState(false);
// Debounce search term so that it only gives us latest value …
// … if searchTerm has not been updated within last 500ms.
// The goal is to only have the API call fire when user stops typing …
// … so that we aren’t hitting our API rapidly.
const debouncedSearchTerm = useDebounce(searchTerm, 500);

// Effect for API call
useEffect(
() => {
if (debouncedSearchTerm) {
setIsSearching(true);
searchCharacters(debouncedSearchTerm).then(results => {
setIsSearching(false);
setResults(results.data.results);
});
} else {
setResults([]);
}
},
[debouncedSearchTerm] // Only call effect if debounced search term changes
);

return (

setSearchTerm(e.target.value)}
/>

{isSearching &&

Searching …

}

{results.map(result => (

{result.title}

))}

);
}

// API search function
function searchCharacters(search) {
const apiKey = ‘f9dfb1e8d466d36c27850bedd2047687’;
return fetch(
https://gateway.marvel.com/v1/public/comics?apikey=${apiKey}&titleStartsWith=${search},
{
method: ‘GET’
}
).then(r => r.json());
}

// Hook
function useDebounce(value, delay) {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);

  // Cancel the timeout if value changes (also on delay change or unmount)
  // This is how we prevent debounced value from updating if value is changed ...
  // .. within the delay period. Timeout gets cleared and restarted.
  return () => {
    clearTimeout(handler);
  };
},
[value, delay] // Only re-call effect if value or delay changes

);

return debouncedValue;
}


November 09, 2018•[Open in CodeSandbox][47]•[Suggest a change][48] ## [useOnScreen][49] This hook allows you to easily detect when an element is visible on the screen as well as specify how much of the element should be visible before being considered on screen. Perfect for lazy loading images or triggering animations when the user has scrolled down to a particular section.

import { useState, useEffect, useRef } from ‘react’;

// Usage
function App() {
// Ref for the element that we want to detect whether on screen
const ref = useRef();
// Call the hook passing in ref and root margin
// In this case it would only be considered onScreen if more …
// … than 300px of element is visible.
const onScreen = useOnScreen(ref, ‘-300px’);

return (

Scroll down to next section 👇


{onScreen ? (

Hey I’m on the screen

通过简单的示例来理解React Hook

) : (

Scroll down 300px from the top of this section 👇

)}

);
}

// Hook
function useOnScreen(ref, rootMargin = ‘0px’) {
// State and setter for storing whether element is visible
const [isIntersecting, setIntersecting] = useState(false);

useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
// Update our state when observer callback fires
setIntersecting(entry.isIntersecting);
},
{
rootMargin
}
);
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.unobserve(ref.current);
};
}, []); // Empty array ensures that effect is only run on mount and unmount

return isIntersecting;
}


November 08, 2018•[Open in CodeSandbox][50]•[Suggest a change][51] ## [usePrevious][52] One question that comes up a lot is _"When using hooks how do I get the previous value of props or state?"_. With React class components you have the componentDidUpdate method which receives previous props and state as arguments or you can update an instance variable (this.previous = value) and reference it later to get the previous value. So how can we do this inside a functional component that doesn't have lifecycle methods or an instance to store values on? Hooks to the rescue! We can create a custom hook that uses the useRef hook internally for storing the previous value. See the recipe below with inline comments. You can also find this example in the official [React Hooks FAQ][53].

import { useState, useEffect, useRef } from ‘react’;

// Usage
function App() {
// State value and setter for our example
const [count, setCount] = useState(0);

// Get the previous value (was passed into hook on last render)
const prevCount = usePrevious(count);

// Display both current and previous count value
return (

Now: {count}, before: {prevCount}

);
}

// Hook
function usePrevious(value) {
// The ref object is a generic container whose current property is mutable …
// … and can hold any value, similar to an instance property on a class
const ref = useRef();

// Store current value in ref
useEffect(() => {
ref.current = value;
}, [value]); // Only re-run if value changes

// Return previous value (happens before update in useEffect above)
return ref.current;
}


November 07, 2018•[Open in CodeSandbox][54]•[Suggest a change][55] ## [useOnClickOutside][56] This hook allows you to detect clicks outside of a specified element. In the example below we use it to close a modal when any element outside of the modal is clicked. By abstracting this logic out into a hook we can easily use it across all of our components that need this kind of functionality (dropdown menus, tooltips, etc).

import { useState, useEffect, useRef } from ‘react’;

// Usage
function App() {
// Create a ref that we add to the element for which we want to detect outside clicks
const ref = useRef();
// State for our modal
const [isModalOpen, setModalOpen] = useState(false);
// Call hook passing in the ref and a function to call on outside click
useOnClickOutside(ref, () => setModalOpen(false));

return (

{isModalOpen ? (

👋 Hey, I’m a modal. Click anywhere outside of me to close.

) : (

)}

);
}

// Hook
function useOnClickOutside(ref, handler) {
useEffect(() => {
const listener = event => {
// Do nothing if clicking ref’s element or descendent elements
if (!ref.current || ref.current.contains(event.target)) {
return;
}

handler(event);
};

document.addEventListener(‘mousedown’, listener);
document.addEventListener(‘touchstart’, listener);

return () => {
document.removeEventListener(‘mousedown’, listener);
document.removeEventListener(‘touchstart’, listener);
};
}, []); // Empty array ensures that effect is only run on mount and unmount
}


Also check out: + [Andarist/use-onclickoutside][57] - Similar logic implemented as a library. Also accounts for passive events. Good choice if you want to pull something from github/npm. November 05, 2018•[Open in CodeSandbox][58]•[Suggest a change][59] ## [useAnimation][60] This hook allows you to smoothly animate any value using an easing function (linear, elastic, etc). In the example we call the useAnimation hook three times to animated three balls on to the screen at different intervals. Additionally we show how easy it is to compose hooks. Our useAnimation hook doesn't actual make use of useState or useEffect itself, but instead serves as a wrapper around the useAnimationTimer hook. Having the timer logic abstracted out into its own hook gives us better code readability and the ability to use timer logic in other contexts. Be sure to check out the [CodeSandbox Demo][61] for this one.

import { useState, useEffect } from ‘react’;

// Usage
function App() {
// Call hook multiple times to get animated values with different start delays
const animation1 = useAnimation(‘elastic’, 600, 0);
const animation2 = useAnimation(‘elastic’, 600, 150);
const animation3 = useAnimation(‘elastic’, 600, 300);

return (

);
}

const Ball = ({ innerStyle }) => (

);

// Hook
function useAnimation(
easingName = ‘linear’,
duration = 500,
delay = 0
) {
// The useAnimationTimer hook calls useState every animation frame …
// … giving us elapsed time and causing a rerender as frequently …
// … as possible for a smooth animation.
const elapsed = useAnimationTimer(duration, delay);
// Amount of specified duration elapsed on a scale from 0 – 1
const n = Math.min(1, elapsed / duration);
// Return altered value based on our specified easing function
return easingeasingName;
}

// Some easing functions copied from:
// https://github.com/streamich/ts-easing/blob/master/src/index.ts
// Hardcode here or pull in a dependency
const easing = {
linear: n => n,
elastic: n =>
n * (33 * n * n * n * n – 106 * n * n * n + 126 * n * n – 67 * n + 15),
inExpo: n => Math.pow(2, 10 * (n – 1))
};

function useAnimationTimer(duration = 1000, delay = 0) {
const [elapsed, setTime] = useState(0);

useEffect(
() => {
let animationFrame, timerStop, start;

  // Function to be executed on each animation frame
  function onFrame() {
    setTime(Date.now() - start);
    loop();
  }

  // Call onFrame() on next animation frame
  function loop() {
    animationFrame = requestAnimationFrame(onFrame);
  }

  function onStart() {
    // Set a timeout to stop things when duration time elapses
    timerStop = setTimeout(() => {
      cancelAnimationFrame(animationFrame);
      setTime(Date.now() - start);
    }, duration);

    // Start the loop
    start = Date.now();
    loop();
  }

  // Start after specified delay (defaults to 0)
  const timerDelay = setTimeout(onStart, delay);

  // Clean things up
  return () => {
    clearTimeout(timerStop);
    clearTimeout(timerDelay);
    cancelAnimationFrame(animationFrame);
  };
},
[duration, delay] // Only re-run effect if duration or delay changes

);

return elapsed;
}


November 02, 2018•[Open in CodeSandbox][62]•[Suggest a change][63] ## [useWindowSize][64] A really common need is to get the current size of the browser window. This hook returns an object containing the window's width and height. If executed server-side (no window object) the value of width and height will be undefined.

import { useState, useEffect } from ‘react’;

// Usage
function App() {
const size = useWindowSize();

return (

{size.width}px / {size.height}px

);
}

// Hook
function useWindowSize() {
const isClient = typeof window === ‘object’;

function getSize() {
return {
width: isClient ? window.innerWidth : undefined,
height: isClient ? window.innerHeight : undefined
};
}

const [windowSize, setWindowSize] = useState(getSize);

useEffect(() => {
if (!isClient) {
return false;
}

function handleResize() {
  setWindowSize(getSize());
}

window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);

}, []); // Empty array ensures that effect is only run on mount and unmount

return windowSize;
}


October 31, 2018•[Open in CodeSandbox][65]•[Suggest a change][66] ## [useHover][67] Detect whether the mouse is hovering an element. The hook returns a ref and a boolean value indicating whether the element with that ref is currently being hovered. So just add the returned ref to any element whose hover state you want to monitor.

import { useRef, useState, useEffect } from ‘react’;

// Usage
function App() {
const [hoverRef, isHovered] = useHover();

return (

{isHovered ? ‘😁’ : ‘☹️’}

);
}

// Hook
function useHover() {
const [value, setValue] = useState(false);

const ref = useRef(null);

const handleMouseOver = () => setValue(true);
const handleMouseOut = () => setValue(false);

useEffect(
() => {
const node = ref.current;
if (node) {
node.addEventListener(‘mouseover’, handleMouseOver);
node.addEventListener(‘mouseout’, handleMouseOut);

    return () => {
      node.removeEventListener('mouseover', handleMouseOver);
      node.removeEventListener('mouseout', handleMouseOut);
    };
  }
},
[ref.current] // Recall only if ref changes

);

return [ref, value];
}


October 30, 2018•[Open in CodeSandbox][68]•[Suggest a change][69] ## [useLocalStorage][70] Sync state to local storage so that it persists through a page refresh. Usage is similar to useState except we pass in a local storage key so that we can default to that value on page load instead of the specified initial value.

import { useState, useEffect } from ‘react’;

// Usage
function App() {
// Similar to useState but we pass in a key to value in local storage
// With useState: const [name, setName] = useState(‘Bob’);
const [name, setName] = useLocalStorage(‘name’, ‘Bob’);

return (

setName(e.target.value)}
/>

);
}

// Hook
function useLocalStorage(key, initialValue) {
// The initialValue arg is only used if there is nothing in localStorage …
// … otherwise we use the value in localStorage so state persist through a page refresh.
// We pass a function to useState so localStorage lookup only happens once.
// We wrap in try/catch in case localStorage is unavailable
const [item, setInnerValue] = useState(() => {
try {
return window.localStorage.getItem(key)
? JSON.parse(window.localStorage.getItem(key))
: initialValue;
} catch (error) {
// Return default value if JSON parsing fails
return initialValue;
}
});

// Return a wrapped version of useState’s setter function that …
// … persists the new value to localStorage.
const setValue = value => {
setInnerValue(value);
window.localStorage.setItem(key, JSON.stringify(item));
};

// Alternatively we could update localStorage inside useEffect …
// … but this would run every render and it really only needs …
// … to happen when the returned setValue function is called.
/*
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(item));
});
*/

return [item, setValue];
}
“`
October 29, 2018•Open in CodeSandboxSuggest a change

作者:笑在秋风中
链接:https://juejin.im/entry/5c3c688351882525db1439d5

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:通过简单的示例来理解React Hook

链接:https://www.javascriptc.com/3823.html

« webpack-dev-middleware 源码解读
Node.js Web 开源框架 Fastify 简介»
Flutter 中文教程资源

相关推荐

QR code