React Hooks

useState

const [state, setState] = useState(initialState)

说明:可在函数组件中管理状态

参数:

  1. state:状态值
  2. setState:更改state状态的方法
  3. initialState:状态的初始值

在第一次渲染组件的时候,会将initialState的值赋给state,当需要更改状态值时可以使用setState(xxx)传入要更改的值

例:

function App() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <h1>You clicked {count} times</h1>
      <button onClick={() => setCount(count + 1)}> +1 </button>
      <button onClick={() => setCount(count - 1)}> -1 </button>
  )
}

函数式更新

新的state需要通过使用先前的state计算得出的话,就可以传递一个函数给setState。这个函数能接收先前的state,并返回一个更新后的值

function App() {
	const [count, setCount] = useState(0)
	
	return (
		<div>
		<h1>You clicked {count} times</h1>
		<button onClick={() => setCount(preState => preState + 1)}>函数式写法</button>	//此处的preState的值就更新前的值
		</div>
	)
}

注意
由于与class组件中的setState方法不同,useState不会自动合并更新对象。需要配合使用展开运算符来达到合并更新对象的效果。

const [state, setState] = useState({});
setState(prevState => {
 // 也可以使用 Object.assign
return {...prevState, ...updatedValues};
});

惰性初始 state

由于在组件初始化的时候,会将initialState作为初始值赋给state,并且该值也只在组件初始化中起作用,在后续渲染中会被忽略。

倘若初始state需要通过复杂的计算得到的话,可以在将initialState替换成一个函数,然后在该函数中计算并返回值,同样的这个函数也只有在组件初次渲染中起作用。

例:

function App() {
  const [count, setCount] = useState(() => {
    const initialState = (10 - 9) * 5 / 3
    return initialState
  })
  return (
    <div>
      <h1>You clicked {count} times</h1>
    </div>
  )
}

在这里插入图片描述

react是怎么保证多个useState的相互独立的?

当定义并且调用了多个useState,传的参数也都是一个值时,也没有告诉react这些值对应的key,那么react是怎么保证多个useState都能找到其对应的state呢?

答案是:react按照定义useState的顺序来确定的

例:

  //第一次渲染
  useState(42);  //将age初始化为42
  useState('banana');  //将fruit初始化为banana
  useState([{ text: 'Learn Hooks' }]); //...

  //第二次渲染,会根据顺序来替换数据
  useState(42);  //读取状态变量age的值(这时候传的参数42直接被忽略)
  useState('banana');  //读取状态变量fruit的值(这时候传的参数banana直接被忽略)
  useState([{ text: 'Learn Hooks' }]); //...

假如更改代码:

let showFruit = true;
function ExampleWithManyStates() {
  const [age, setAge] = useState(42);
  
  if(showFruit) {
    const [fruit, setFruit] = useState('banana');
    showFruit = false;
  }
 
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

这样一来:

//第一次渲染
useState(42);  //将age初始化为42
useState('banana');  //将fruit初始化为banana
useState([{ text: 'Learn Hooks' }]); //...

//第二次渲染
useState(42);  //读取状态变量age的值(这时候传的参数42直接被忽略)
// useState('banana');    这个useState就会读取不到
useState([{ text: 'Learn Hooks' }]); //此时这里的useState读取到的却是状态变量fruit的值,导致报错

因此,react规定我们必须要把hooks写在函数的最外层,不能写在一些条件语句中,以保证hooks的执行顺序是一致的。

useEffect

useEffect(function,[])

说明:可以在函数组件中进行副作用操作

参数:

  1. function:在组件第一次渲染或者后续的更新渲染中要进行的副作用;该函数可以有返回值,若有返回值,则返回的也需是个函数,并且该函数会被用来清除副作用(在组件被销毁时执行)

  2. [ ](可选):若没有该参数,则会在每一次组件更新时,执行副作用函数;若参数为空数组([]),则只会在组件第一次渲染时执行副作用函数;若参数的数组中有值,则只有当该值发生变化的时候会执行副作用函数。

useEffect(() => {
  document.title = `You clicked ${count} times`   //副作用函数
  return () => {
    console.log('cleanup')   //清除副作用函数
  }
})  //在每一次组件重新渲染时都会调用
//-------------------------------------------------
useEffect(() => {
  document.title = `You clicked ${count} times`   //副作用函数
  return () => {
    console.log('cleanup')   //清除副作用函数
  }
}, [])  //在第一次渲染执行副作用函数
//-------------------------------------------------
useEffect(() => {
  document.title = `You clicked ${count} times`   //副作用函数
  return () => {
    console.log('cleanup')   //清除副作用函数
  }
}, [count]) //只有当count发生改变时才会执行副作用函数

useContext

const context = useContext(MyContext)

说明:可以帮助我们跨越组件层级直接传递变量,实现数据共享。

参数:

  1. MyContext:用React.createContext()创建的上下文对象

它会返回由MyContext传递过来的上下文值。

例:

import React, { useState, createContext, useContext } from 'react';
import ReactDOM from 'react-dom';


const CountContext = createContext(0);  // 创建一个上下文,默认值为0

const App = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>父组件点击数量:{count}</p>
      <button onClick={() => setCount(count + 1)}>{"点击+1"}</button>
      <CountContext.Provider value={count}> {/* 使用CountContext.Provider包裹需要接收参数的子组件,并通过value将count传给子组件 */}
        <Counter />
      </CountContext.Provider>
    </div>
  );
};

const Counter = () => {
  const count = useContext(CountContext); // 使用useContext获取上下文中的值,这里的值就是父组件传递的count

  return (
    <h1>子组件获得的点击数量:{count}</h1>
  );
};


ReactDOM.render(
  <App />,
  document.getElementById('root')
);

useReducer()

const [state,dispatch] = useReducer(reducer, initalArg, init)

说明:它是useState的替代方案。能接收一个(state,action)=> newStatereducer,并且返回当前的state以及其对应配套的dispatch方法。

参数:

  1. reducer:reducer函数,(state,action)=> newState
  2. initialArg:参数初始值
  3. init:函数,用来对初始值进行复杂计算

例:

const App = () => {

  const [count, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'increment':
        return state + 1;
      case 'decrement':
        return state - 1;
      default:
        throw new Error();
    }
  }, 0);
  return (
    <div>
      <h1>You clicked {count} times</h1>
      <button onClick={() => { dispatch({ type: 'increment' }) }}>+ </button>
      <button onClick={() => { dispatch({ type: 'decrement' }) }}>- </button>
    </div>
  );
};

当点击+按钮时,会根据type值去匹配useReducer中的第一个参数reducer函数里面的action.type,然后返回其对应的值;
state的值就是第二个参数给定的初始值

再有例:

const App = () => {

  const initialValue = {
    count: 0,
    name: 'How',
  };
  const [state, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'increment':
        return { count: state.count + 1, name: `increment${state.count}` };
      case 'decrement':
        return { count: state.count - 1, name: `decrement${state.count}` };
      default:
        throw new Error();
    }
  }, initialValue);

  return (
    <div>
      <h1>You clicked {state.count} times</h1>
      <h1>You name:{state.name}</h1>
      <button onClick={() => { dispatch({ type: 'increment' }) }}>+ </button>
      <button onClick={() => { dispatch({ type: 'decrement' }) }}>- </button>
    </div>
  );
};

可以定义个对象然后当做初始值传给useReducer当做第二个参数,同样的该对象也会赋值给第一个参数函数中的state。

惰性初始化

useReducer也可以像useState一样来惰性初始化state。当将第三个参数init函数传入时,这样初始state就被设置为init(initialArg)

const App = () => {
  const [count, setCount] = useState(100);
  return (
    <AppF count={count} />
  )
}

const AppF = ({ count }) => {

  const [state, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'increment':
        return { count: state.count + 1, name: `increment${state.count}` };
      case 'decrement':
        return { count: state.count - 1, name: `decrement${state.count}` };
      default:
        throw new Error();
    }
  }, count, (count) => {
    return { count: count + 99, name: `initial${count}` };
  });

  return (
    <div>
      <h1>You clicked {state.count} times</h1>
      <h1>You name:{state.name}</h1>
      <button onClick={() => { dispatch({ type: 'increment' }) }}>+ </button>
      <button onClick={() => { dispatch({ type: 'decrement' }) }}>- </button>
    </div>
  );
};

将外部传来值进行处理然后再将处理好的数据赋值给state
传来的count是0,进行了处理后值变为199
在这里插入图片描述

useMemo()

uesMemo(()=>{},[])

说明:一种性能优化的手段,可以减少不必要的渲染

参数:

  1. 函数:该函数需要有一个返回值,并且返回值就是useMemo的返回值
  2. [ ]:依赖项,当里面的依赖项发生变化了,就会引发组件的重渲染并且重新执行上面的函数。

例:

import React, { useState, useMemo, memo } from 'react';
import ReactDOM from 'react-dom';


let Con = memo((props) => {
  console.log("render Con")
  return (<div>{props.value}</div>)
})

function App() {
  const [count, setCount] = useState(0);
  const [value, setValue] = useState(0);

  const cachedValue = useMemo(function () {
    return count + 1
  }, [count])   // 只有当count变化时才会执行

  return (
    <>
      <div>{count}</div>
      <div> {value}</div>
      <Con value={cachedValue} />
      <button onClick={() => setCount(v => v + 1)}>Add Count</button>
      <button onClick={() => setValue(v => v + 1)}>Add Value</button>
    </>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

useCallback()

useCallback(()=>{},[])

说明:useCallback就是useMemo的语法糖

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

参数:

  1. 函数:回调函数,需要执行的代码语句
  2. [ ]:依赖项,当里面的依赖项发生变化了,就会引发组件的重渲染并且重新执行上面的函数。

例:

function App() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
  	setCount(count + 1)
  }, [count]);

  return (
    <>
      <div>{count}</div>
      <button onClick={increment}>Add Count</button>
    </>
  );
}

useRef()

const refContainer = useRef(initialValue)

说明:能获得相对应的DOM节点

参数:

  • initialValue:默认值

它会返回一个可变的ref对象,并且这个对象再组件的整个生命周期中是持续存在的,该对象只有一个current属性,其初始值就是传入的initialValue

function App() {

  const inputRef = useRef();

  const getInput = () => {
    console.log(ref);
    console.log(ref.current);
  }
  return (
    <>
      <button onClick={getInput}>获取input</button>
      <input ref={inputRef} style={{ border: '1px solid red' }} />
    </>
  );
}

在这里插入图片描述
例:

当我们需要点击按钮时获得这个输入框的焦点

function App() {

  const inputRef = useRef();

  const getInput = () => {
    inputRef.current.focus();
  }
  return (
    <>
      <button onClick={getInput}>获取input</button>
      <input ref={inputRef} style={{ border: '1px solid red' }} />
    </>
  );
}

forwardRef()和useImperativeHandle()

forwardRef:可以接收父组件传递过来的ref

React.forwardRef((props, ref) => {})  
//创建一个React组件,
//这个组件将会接受到父级传递的ref属性,
//可以将父组件创建的ref挂到子组件的某个dom元素上,
//然后在父组件就可以通过该ref就能获取到该dom元素

例:

const App = () => {

  const sonInput = useRef(null);

  return (
    <>
      <h1>父组件</h1>
      <br />
      <h1>子组件</h1>
      <Son ref={sonInput} />
    </>
  )
}
const Son = forwardRef((props, ref) => {
  return (
    <>
      <input ref={ref} />	//将父组件传递过来的ref赋值给input
      <button onClick={() => {
        console.log(ref.current)	//点击按钮就可以获得当前input节点
      }
      }>点击</button>
    </>
  )
})

但是使用forwardRef+useRef来获取函数组件DOM时,获取的DOM的属性暴露了太多,此时就可以使用useImperativeHandle来解决。

useImperativeHandle()可以用来实现在函数组件的父组件中调用子组件的状态或者方法,可以自定义暴露给父组件

useImperativeHandle(ref, createHandle, [deps])

参数:

  • ref: 由父组件传来的ref值
  • createHandle:是一个函数,会返回一个对象,这个对象中的属性会全部挂载在第一个参数refcurrent属性上。
  • [deps]:依赖项,与useEffect,useCallback一样,当依赖项发生变化时,第二个参数就会重新执行,并且还会重新将属性挂载到第一个参数上面去。

例:

const App = () => {

  const sonInput = useRef(null);

  return (
    <>
      <Son ref={sonInput} />    {/* 将创建的ref传递给子组件*/}
      <button onClick={() => sonInput.current.submit()}>submit</button>    {/* 调用子组件中的submit方法 */}
      <button onClick={() => sonInput.current.randomValue()}>生成一个随机数</button>    {/* 调用子组件中的randomValue方法 */}
    </>
  )
}
const Son = forwardRef((props, ref) => {

  const [value, setValue] = useState('');

  const randomValue = useCallback(() => {
    setValue(Math.round(Math.random() * 100) + '');
  }, [])

  const submit = useCallback(() => {
    if (value) {
      alert(`提交成功${value}`);
    } else {
      alert('请输入内容');
    }
  }, [value])

  useImperativeHandle(ref, () => {    //获取父组件传来的ref,并且暴露randomValue,submit这两个方法给父组件
    return {
      randomValue,
      submit
    }
  }, [randomValue, submit])

  return (
    <div className="box">
      <h2>子组件</h2>
      <section>
        <label>用户名:</label>
        <input
          value={value}
          placeholder="请输入用户名"
          onChange={e => setValue(e.target.value)}
        />
      </section>
      <br />
    </div>
  )
})

useLayoutEffect()

useLayoutEffect(() => { doSomething })

useEffect Hooks 类似,都是执行副作用操作。但是它是在所有 DOM 更新完成后触发。可以用来执行一些与布局相关的副作用,比如获取 DOM 元素宽高,窗口滚动距离等等。

注意:进行副作用操作时尽量优先选择 useEffect,以免阻止视觉更新。

其用法与useEffect相似,但是会在useEffect之前执行

例:

const App = () => {
  const divRef = useRef(null);

  const [height, setHeight] = useState(100);

  useLayoutEffect(() => {
    // DOM 更新完成后打印出 div 的高度
    console.log('useLayoutEffect: ', divRef.current.clientHeight);
  })

  return <>
    <div ref={divRef} style={{ background: 'red', height: height }}>Hello</div>
    <button onClick={() => setHeight(height + 50)}>改变 div 高度</button>
  </>
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Maike.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值