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}
      
        Reset
      
      
      
    >
  );
}[/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