学习新东西的时候,首先要问自己两个问题?
-
- 为什么会存在这个东西?
-
- 这个东西能解决什么问题?
1.为什么会存在这个东西?
react的核心是组件。v16.8版本之前,组件的标准写法是类(class)。
大型组件很难拆分和重构,也很难测试;业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑;组件类引入了复杂的编程模式,比如 render props 和 高阶组件。
react团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。组件的最佳写法应该是函数,而不是类。
但是函数组件有重大限制,必须是纯函数,不能包含状态,也不支持生命周期方法,因此无法取代类。
`react hooks 的设计目的,就是加强函数组件,完全不使用类,就能写出一个全功能的组件`
2.这个东西能解决什么问题?
前言
由于大型组件类的难拆分性,重复逻辑和复杂的编程模式。编写函数组件好拆分、可复用性强。但是有限制性局限性。所以在函数组件中,如果需要外部功能和副作用,就需要钩子把外部代码“钩”进来。
概念
useState是一个Hook函数,让你在函数组件中拥有state变量。它接收一个初始化的state,返回是一个数组,数组里有两个元素,第一个元素是当前状态值和另一个更新该值的方法。
用法
const [state, setState] = useState(initialState)
规则
useState 和所有hooks一样,都遵循同样的规则:
- 在顶层调用hooks。不能在循环、条件或者内嵌方法中调用hooks。因为react依赖被调用的顺序来获取特定变量的正确值。
- 在函数组件中调用hooks,不要在类组件中使用hooks.
案例1
基础使用
const Parent = () => {
const [count, setCount] = useState(0)
return (
<>
<h1>parent</h1>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<div>--------------------</div>
<Child count={count} setCount={setCount} />
</>
)
}
const Child = ({ count, setCount }) => {
return (
<>
<h1>child</h1>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</>
)
}
更新方法不会立即更新值
如果你需要用到之前的值更新状态,你必须将之前的值传递给该方法,则它会返回一个更新后的值,eg: setMessage(previousVal => previousVal + currentVal)
function Case2 () {
const [count, setCount] = useState(0);
const [count1, setCount1] = useState(0);
function A () {
setCount(count + 1)
setCount(count + 1)
}
function B () {
setCount1(pre => pre + 1)
setCount1(pre => pre + 1)
}
return <div>
<h1>double click</h1>
<h2>count + 1</h2>
<p>You clicked {count} times</p>
<button onClick={() => A()}>click</button>
<div>--------------------------</div>
<h2>prev => prev + 1</h2>
<p>You clicked {count1} times</p>
<button onClick={() => B()}>click</button>
</div>
}
初始值
useState接收状态的初始值作为一个参数。
初始化的值仅仅会在第一次渲染时被赋值(如果他是一个函数,也是会在初次渲染时执行)。
在后续的更新中(由于组件本身的状态更改或者是说父组件导致的变化),useStateHook参数(初始值)将会被忽略,当前的值将会被使用。
1.1 初始值通过函数传递
const Message = () => {
const messageState = useState(() => expensiveComputation())
}
1.2 初始值通过组件函数参数传递(props)
const Message = (props) => {
const messageState = useState(props.message)
}
function Case3 () {
function init () {
return 1
}
const [count, setCount] = useState(() => init());
const increase = () => {
setCount(count + 1);
}
const decrease = () => {
setCount(count - 1);
}
return <div className="container">
<button className="chevron chevron-up" onClick={ decrease } />
<h1>{ count }</h1>
<button className="chevron chevron-down" onClick={ increase } />
</div>
}
使用对象作为状态变量
- 不可变的重要性
useState返回的更新方法不是像类组件中的setState合并对象
function Case4 () {
const [state, setState] = useState({ count: 0, name: 'jack' })
function A () {
setState(prevState => ({...prevState, count: prevState.count + 1}))
}
function B () {
setState(prevState => prevState.count + 1)
}
useEffect(() => {
console.log(state)
})
return <div>
<h3>{ state.count }</h3>
<h3>{ state.name }</h3>
<h2>corret set</h2>
<button onClick={() => A()}>set</button>
<h2>wrong set</h2>
<button onClick={() => B()}>set</button>
</div>
}
在某些情况下,拷贝深度内嵌对象是比较昂贵的,因为React可能会依赖那些没有改变过的字段值重新渲染你应用的部分内容。
对于这个原因,首先要做的是尝试扁平化你的对象。需要关注的是,React官方推荐根据哪些值倾向于一起变化,将状态分割成多个状态变量。
如果这个不可能的话,推荐使用第三方库来帮助你使用不可变对象,例如immutable.js或者 immer