[React] 函数组件

69 阅读3分钟

创建一个函数组件

const Hello = (props) => {
  return <div>{props.message}</div>
}

const Hello = props => <div>{props.message}</div>

function Hello(props){
  return <div>{props.message}</div>
}

Hooks

useState

  1. setN一定会触发重新渲染
  2. 数据会被存入新的n
function App() {
  const [n, setN] = React.useState(0);
  return (
    <div className="App">
      <p>{n}</p>
      <p>
        <button onClick={() => setN(n + 1)}>+1</button>
      </p>
    </div>
  );
}
function App() {
  const [n, setN] = useState(0)
  const onClick = ()=>{
    setN(n+1)
    setN(n+1) // 你会发现 n 不能加 2
    // setN(i=>i+1)
    // setN(i=>i+1) n可以+2
  }
  return (
    <div className="App">
      <h1>n: {n}</h1>
      <button onClick={onClick}>+2</button>
    </div>
  );
}

useReducer

Flux/Redux思想

useState的复杂版,常用于表单

  1. 创建初始值
  2. 创建操作合集reducer(state,action)
  3. 传给useReducer,得到读写API
  4. 调用写API,传一个type ({type:'操作类型'})

代替Redux

  1. 将数据集中在一个store对象
  2. 将操作集中在reducer
  3. 创建一个Context
  4. 创建对数据的读写api
  5. 将读写api放入Context
  6. 用Context.Provider把api提供给所有组件
  7. 各个组件用useContext获取读写api
const initial = {
  n: 0
};

const reducer = (state, action) => {
  if (action.type === "add") {
    return { n: state.n + action.number };
  } else if (action.type === "multi") {
    return { n: state.n * 2 };
  } else {
    throw new Error("unknown type");
  }
};

function App() {
  const [state, dispatch] = useReducer(reducer, initial);
  const { n } = state;
  const onClick = () => {
    dispatch({ type: "add", number: 1 });
  };
  const onClick2 = () => {
    dispatch({ type: "add", number: 2 });
  };
  return (
    <div className="App">
      <h1>n: {n}</h1>
      <button onClick={onClick}>+1</button>
      <button onClick={onClick2}>+2</button>
    </div>
  );
}

useContext

上下文,相当于在某一范围中充当「全局变量」的作用

从上至下逐级更新的过程,不是响应式过程(监听数据变化并做出反应)

  1. 创建一个Context
const C = createContext(null)
  1. 用C.Provider圈定范围
<C.Provider value={}>
  const [ n, setN ] = useState(0)
  <ComponentA />
  <ComponentB />
</C.Provider>
  1. 在组件中使用
const { n, setN } = useContext(C)

useEffect

每次render后调用的函数

在函数组件中,作为componentDidMount, componentDidUpdate, componentWillUnmount使用

多个useEffect会按写代码时的先后顺序执行

useEffect(() => {})  //每次render后执行

useEffect(() => {}, [])  //第一次渲染后执行

useEffect(() => {}, [n])  //n变化后执行
//如果数组中包含了所有的变量,那与第一种写法等价

useEffect(() => { return () => {}})  //return的函数会在该组件被噶了之前执行

useLayoutEffect

在浏览器改变外观前执行,区别于useEffect在浏览器改变外观之后

因此useLayoutEffect总是比useEffect先执行

useMemo

React.memo(ComponentA): ComponentA只在props更新后执行

const fn = useMemo(() => {
  return () => {}
}, [m,n])

缓存内容,在页面刷新时缓存上一次的值,并使新渲染的组件使用该缓存值,新fn和旧fn为相同的两个空函数。只有m或者n变化时,fn才会重新得到新值

如果不使用useMemo,两次得到的fn会因为函数地址不同而被认为是两个不同的函数,从而触发组件的渲染,增加了渲染量,降低了性能

useCallback

useMemo简化版

const fn = useCallback(() => {}, [m,n])

useRef

让一个值在组件不断render时保持不变

const count = useRef(0)

useEffect(()=>{
  count.current += 1
})

此时count记录了组件渲染的次数

使用.current的原因是:使用引用,来确保每次的渲染得到的新count都是指向同一个对象的引用

[scode type="yellow" size=""]count.current的变化不会触发UI的更新,需要调用useState来手动更新[/scode]

const [随便取一个,set随便取一个] = useState(null)

const onClick = () => {
  count.current += 1
  set随便取一个(Math.random())
 }
 
 //这样才会触发UI更新

forwardRef

React的函数组件需要使用forwardRef来获取外部传来的ref

function App() {
  const buttonRef = useRef(null)
  return(
    <div>
      <Button2 ref={buttonRef}>button</Button2>
    </div>
  )
}

const Button2 = React.forwardRef((props,ref) => {
  return (<button ref={ref} {...props} />)
}

useImperativeHandle

自定义ref