react -hook

react 复用 render Props 、 High order component 和 Hook

[cc lang=”js”]// render props
class Mouse Tracker extends React.Component {
render ( ) {
return (
< div >
< h1 > 移动鼠标! </ h1 >
< Mouse render = { mouse => (
// Mouse 组件内 props.render(mouse) , Mouse 组件为props.render函数提供 mouse 坐标参数,渲染内容为render组件内容

) } />
// render 函数因为是一个函数直接量, 更新时每次都是不同的, 可以定义一个函数变量

) ;
}
} [/cc]

React Hook

[cc lang=”js”]

componentDidMount() {
ChatAPI.subscribeToFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}

componentWillUnmount() {
ChatAPI.unsubscribeFromFriendStatus(
this.props.friend.id,
this.handleStatusChange
);
}

// 如果 props.friend 发生变化,这将导致订阅不能取消, 从而导致内存泄露或崩溃的问题
// useEffect 增加didUpdate,解决了 class 组件中因为没有处理更新逻辑而导致常见的 bug
useEffect(() => {
// …
ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
react hook 解决更新中重新订阅,并删除上一次订阅
[/cc]

!! 通过跳过 Effect 进行性能优化
prevProps 或 prevState
[cc lang=”js”]
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}[/cc]
Effect 加入第二个参数
[cc]
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新[/cc]

!!Effect 第二个参数
// 依赖列表中省略函数

!!如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组 []

1 如果函数里没有用到 props 或state, 第二个参数可以是 [], 这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行

2. 如果函数里用到了, 第二个参数 需要加入 [someProp]

 3. setState 的函数式更新形式 解决性能问题,

*******关注点分离 创建多个 effect 干不同的事
[cc lang=”js”]
function FriendStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});

const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}

ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
// …
}[/cc]

**************只在最顶层使用 Hook
不要在循环,条件或嵌套函数中调用 Hook

*********自定义hook
[cc lang=”js”]
import React, { useState, useEffect } from ‘react’;

function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);

useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}

ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});

return isOnline;
}

// 使用 hook
function FriendListItem(props) {
const isOnline = useFriendStatus(props.friend.id);

return (

  • {props.friend.name}
  • );
    }[/cc]

    ******要点
    1 函数式的 setState 结合展开运算符来达到合并更新对象的效果
    [cc lang=”js”]setState(prevState => {
    // 也可以使用 Object.assign
    return {…prevState, …updatedValues};
    });[/cc]

    2 effect
    * effect 的依赖发生变化,它就会被重新创建
    * 请记得 React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect,因此会使得处理额外操作很方便
    * 要记住 effect 外部的函数使用了哪些 props 和 state 很难。这也是为什么 通常你会想要在 effect 内部 去声明它所需要的函数。 这样就能容易的看出那个 effect 依赖了组件作用域中的哪些值
    * 可以通过effect 内部声明变量, 并返回修改变量的函数, 例如组件已经卸载 但请求还在继续,清除卸载hook时设置变量,让状态不更新
    [cc lang=”js”]useEffect(() => {
    let ignore = false;
    async function fetchProduct() {
    const response = await fetch(‘http://myapi/product/’ + productId);
    const json = await response.json();
    if (!ignore) setProduct(json);
    }

    fetchProduct();
    return () => { ignore = true };
    }, [productId]);[/cc]

    ***** 你可以把函数加入 effect 的依赖, 但把它的定义包裹进useCallback Hook。这就确保了它不随渲染而改变,除非 它自身 的依赖发生了改变
    [cc lang=”js”] function ProductPage({ productId }) {
    // ✅ 用 useCallback 包裹以避免随渲染发生改变
    const fetchProduct = useCallback(() => {
    // … Does something with productId …
    }, [productId]); // ✅ useCallback 的所有依赖都被指定了

    return ;
    }

    function ProductDetails({ fetchProduct }) {
    useEffect(() => {
    fetchProduct();
    }, [fetchProduct]); // ✅ useEffect 的所有依赖都被指定了
    // …
    }[/cc]

    ***** 如果我的 effect 的依赖频繁变化,我该怎么办
    [cc lang=”js”]function Counter() {
    const [count, setCount] = useState(0);

    useEffect(() => {
    const id = setInterval(() => {
    setCount(count + 1); // 这个 effect 依赖于 `count` state
    }, 1000);
    return () => clearInterval(id);
    }, []); // ? Bug: `count` 没有被指定为依赖 // 如果指定count 依赖,effect 改变count,effect就会重新执行,死循环

    return

    {count}

    ;
    }
    // setState 的函数式更新形式 ,它允许我们指定 state 该 如何 改变而不用引用 当前 state
    function Counter() {
    const [count, setCount] = useState(0);

    useEffect(() => {
    const id = setInterval(() => {
    setCount(c => c + 1); // ✅ 在这不依赖于外部的 `count` 变量
    }, 1000);
    return () => clearInterval(id);
    }, []); // ✅ 我们的 effect 不适用组件作用域中的任何变量

    return

    {count}

    ;
    }
    [/cc]

    3 useReduce

    你可以选择惰性地创建初始 state。为此,需要将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)
    [cc lang=”js”]
    function init(initialCount) {
    return {count: initialCount};
    }

    function reducer(state, action) {
    switch (action.type) {
    case ‘increment’:
    return {count: state.count + 1};
    case ‘decrement’:
    return {count: state.count – 1};
    case ‘reset’:
    return init(action.payload); //重置 state 的 action 做处理提供了便利
    default:
    throw new Error();
    }
    }

    function Counter({initialCount}) {
    const [state, dispatch] = useReducer(reducer, initialCount, init);
    return (
    <>
    Count: {state.count}




    );
    }[/cc]

    4.useCallback
    把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新
    useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
    [cc lang=”js”]const memoizedCallback = useCallback(
    () => {
    doSomething(a, b);
    },
    [a, b],
    );[/cc]

    5.useMemo
    把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
    记住,传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
    [cc lang=”js”]const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);[/cc]

    6. useRef
    [cc lang=”js”]
    function TextInputWithFocusButton() {
    const inputEl = useRef(null);
    const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
    };
    return (
    <>



    );
    }[/cc]
    请记住,当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现 ref