一篇帖子涵盖所有 React Hooks 和概念!🤗
哦,但是现在 React 中的状态是什么?
useState()
useEffect()
使用上下文()
useReducer()
useReducer() 与 useContext()
使用回调()
useMemo()
useRef()
首先,React 是一个 JS 库,而不是一个完整的框架 🙂 所以,要创建一个完整的 Web 应用程序,你需要了解很多其他可以与 React 结合使用的知识 🤭。在本文中,我将介绍 React 的概念、Hook 以及一些好的实践,当然……😁
我们使用 React 来创建可复用的组件,这些组件可以以合理的方式用于构建 UI。在 React 中创建组件就像创建函数一样简单 🤩。
例如 👇🏻,这是一个简单的 React 组件,我们可以将数据作为参数传递,这些参数可以在函数内部轻松引用。
function Component(props){
return <h1>{props.text}</h1>
}
哦,但是现在 React 中的状态是什么?
状态对象用于存储组件的属性值。当状态对象发生变化时,组件会重新渲染,这基本上使我们能够在应用程序中管理不断变化的数据🆒。
现在让我们使用 useState() 来学习状态。
useState()
const component = () => {
// Tip: use states only inside components
// lets console.log the state and lets see what it returns
console.log(useState(100));
// this will return an array [100,f]
// basically this returns a state and a function to update the state
// we can destructure the array and get the state and the function
const [state, setState] = useState(100);
return (
<div>
hiiieeee
</div>
)
}
但是你不能☠️直接使用 = 运算符更新状态的值,因为这会改变值,但不会重新渲染组件,所以基本上 react 希望😩如果你需要改变状态,你就在 setState 函数中传递值。
在 useState() 中传递函数
// you can also pass function in useState it will set the initial value to what function returns it is useful when you use computationaly high task as initial state
const [state, setState] = useState(() => {
console.log("initial state");
return 100;
});
在 setState() 中传递函数
onClick={() => {
// here value in the props is the state
setState((value) => {
//you can use this method when you need to update the state and you need the previous value of state
return value + 1;
});
}}
useEffect()
使用效果钩子有两个部分,第一个是函数,第二个是依赖数组(可选)
useEffect(()=>{},[])
// we will get a console log every time any state changes.
// for example if you have 2 states in your component and any of
// them changes then we will get the console log
// this is something we mostly dont want.
useEffect(() => {
console.log('change');
})
当您的组件第一次在 DOM 中挂载时,您的第一个 useEffect() 调用将始终运行。
依赖数组🤠
我们可以在 useEffect() 的依赖数组中指定状态,这样它只会监视依赖数组中提到的那些状态的变化😮💨。
const [state1, setState1] = useState(0);
const [state2, setState2] = useState(0);
useEffect(() => {
console.log('state1 changed');
}, [state1])
请记住:如果没有适当的逻辑,请勿更新使用 useEffect() 的状态,否则将创建无限循环🥵
清理功能
useEffect 总是返回一个清理函数,你可以使用它来删除不需要的行为。leanup 函数不仅在我们的组件想要卸载时运行,它还会在执行下一个预定效果之前运行。详细阅读
useEffect(() => {
console.log(`state1 changed | ${state1}`);
return () => {
console.log('state1 unmounted | ', state1);
}
}, [state1])
你可以通过这样的 API 获取数据👇🏻
useEffect(() => {
const url = "https://jsonplaceholder.typicode.com/todos/1";
const fetchData = () => {
fetch(url)
.then(res => res.json())
.then(data => {
setState(data.title)
})
}
fetchData();
}, []);
使用上下文()
Context API 甚至向 React 组件树中最深层的组件提供数据,而无需通过 props 传递数据
import { createContext } from "react";
import { useState } from "react";
const StoreContext = createContext();
const component = () => {
const data = useState({
name: 'Ritesh',
email: 'nyctonio.dev@gmail.com',
})[0];
const Child = () => {
return <div>
<StoreContext.Consumer>
{value => <h1>name is {value.name}</h1>}
</StoreContext.Consumer>
</div>
}
return (
<StoreContext.Provider value={data}>
<Child />
</StoreContext.Provider>
)
}
export default component;
您可以将顶级组件包装在 Context Provider 中,并通过 Context Consumer 在函数内使用它。useContext 的作用是取代 Context Consumer,我们可以直接使用 useContext 获取数据。
请参阅此示例👇🏻。
import { createContext, useContext } from "react";
import { useState } from "react";
const StoreContext = createContext();
const component = () => {
const data = useState({
name: 'Ritesh',
email: 'nyctonio.dev@gmail.com',
})[0];
const Child = () => {
const value = useContext(StoreContext);
return <div>
<h1>name is {value.name}</h1>
</div>
}
return (
<StoreContext.Provider value={data}>
<Child />
</StoreContext.Provider>
)
}
export default component;
useReducer()
useReducer 用于 React 中的状态管理,它有点类似于 javascript 中的 reducer 函数。
// useReducer 函数接受 2 个参数:reducer 函数和 initialState
useReducer(reducer,initialState)
// Reducer 函数接受 2 个参数 currentState 和 action,并返回一个新的 State
Reducer(当前状态,动作)
让我们使用 useReducer 创建一个简单的计数器
import { useReducer } from 'react'
const initialState = 0;
const reducer = (state, action) => {
switch (action) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
return state;
}
}
export default function main() {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch('increment')}>+</button>
<button onClick={() => dispatch('decrement')}>-</button>
</div>
)
}
我们可以通过使我们的状态成为一个对象来使它变得更加复杂
import { useReducer } from 'react'
const initialState = {
firstCounter: 0,
secondCounter: 0
};
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { ...state, firstCounter: state.firstCounter + action.value };
case 'decrement':
return { ...state, firstCounter: state.firstCounter - action.value };
default:
return { ...state };
}
}
export default function main() {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {count.firstCounter}</p>
<button className='bg-gray-200 p-2' onClick={() => dispatch({ type: 'increment', value: 2 })}>
increase by 2
</button>
<button className='bg-gray-200 p-2' onClick={() => dispatch({ type: 'decrement', value: 4 })}>
decrease by 4
</button>
</div>
)
}
或者我们可以使用多个 useReducers 👇🏻
import { useReducer } from 'react'
const initialState = 0;
const reducer = (state, action) => {
switch (action) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
return state;
}
}
export default function main() {
const [count, dispatch] = useReducer(reducer, initialState);
const [count2, dispatch2] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {count}</p>
<button className="bg-gray-100 p-2 m-2"
onClick={() => dispatch('decrement')}>-</button>
<button className="bg-gray-100 p-2 m-2"
onClick={() => dispatch('increment')}>+</button>
<p>Count2: {count2}</p>
<button className="bg-gray-100 p-2 m-2"
onClick={() => dispatch2('increment')}>+</button>
<button className="bg-gray-100 p-2 m-2"
onClick={() => dispatch2('decrement')}>-</button>
</div>
)
}
什么时候使用 useState 以及什么时候使用 useReducer ????
当您具有涉及多个子值的复杂状态逻辑或下一个状态取决于前一个状态时,useReducer 通常比 useState 更可取。 useReducer 还允许您优化触发深度更新的组件的性能,因为您可以向下传递调度而不是回调😎。
useReducer() 与 useContext()
通过使用 useContext 和 useReducer,我们可以在组件树的任何级别管理全局状态,请尝试此示例👇🏻
// main.jsx
import React from 'react'
import { useReducer } from 'react'
import ChildrenA from '../components/ChildrenA';
export const StateContext = React.createContext();
const initialState = 0;
const reducer = (state, action) => {
switch (action) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
return state;
}
}
export default function main() {
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
<StateContext.Provider
value={{ countState: count, countDispatch: dispatch }}>
<ChildrenA />
</StateContext.Provider>
</div >
)
}
// ChildrenA.jsx
import React from 'react'
import ChildrenB from './ChildrenB'
import { StateContext } from '../pages/main'
import { useContext } from 'react'
export default function ChildrenA() {
const { countState, countDispatch } = useContext(StateContext)
return (
<div>
In child A count state is {countState}
<ChildrenB />
</div>
)
}
// ChildrenB.jsx
import React from 'react'
import { StateContext } from '../pages/main'
import { useContext } from 'react'
export default function ChildrenB() {
const { countState, countDispatch } = useContext(StateContext)
return (
<div>
<p>Count is {countState}</p>
<button onClick={() => countDispatch('increment')}>+</button>
<button onClick={() => countDispatch('decrement')}>-</button>
</div>
)
}
你们两个的状态都会同时改变
使用回调()
让我们看一下这段代码,并尝试理解 React 中函数的行为
import React from 'react'
export default function main() {
function Sum() {
return (a, b) => a + b;
}
const func1 = Sum();
const func2 = Sum();
console.log(func1 === func2);
return (
<div>main</div>
)
}
如果您运行此代码,您将在 console.log 中得到 false 信息
现在让我们通过一个例子来理解如何使用 useCallback
// main.jsx
import React, { useState } from 'react'
import ChildrenA from '../components/ChildrenA';
import ChildrenB from '../components/ChildrenB';
import ChildrenC from '../components/ChildrenC';
const main = () => {
const [state1, setState1] = useState(0);
const [state2, setState2] = useState(0);
const handleClickA = () => {
setState1(state1 + 1);
}
const handleClickB = () => {
setState2(state2 + 1);
}
return (
<div className='flex flex-col justify-center items-center'>
<ChildrenA value={state1} handleClick={handleClickA} />
<ChildrenB value={state2} handleClick={handleClickB} />
<ChildrenC />
</div>
)
}
// what react memo do is it re-render the component only when the props change
export default React.memo(main);
// ChildrenA.jsx
import React from 'react'
function ChildrenA({ value, handleClick }) {
console.log('ChildrenA');
return (
<div>ChildrenA {value}
<button className='bg-gray-200 p-2 m-2' onClick={handleClick} >Click</button>
</div>
)
}
export default React.memo(ChildrenA);
// ChildrenB.jsx
import React from 'react'
function ChildrenB({ value, handleClick }) {
console.log('ChildrenB');
return (
<div>ChildrenB {value}
<button className='bg-gray-200 p-2 m-2' onClick={handleClick} >Click</button>
</div>
)
}
export default React.memo(ChildrenB);
// ChildrenC.jsx
import React from 'react'
function ChildrenC() {
console.log('ChildrenC');
return (
<div>ChildrenC</div>
)
}
export default React.memo(ChildrenC);
当你在浏览器中看到 console.log 时,最初所有三个组件都会渲染,但单击任何单击按钮时,只有 2 个组件重新渲染。
注意:这里我们使用了 React.memo(),这就是为什么 ChildrenC 没有重新渲染,因为 props 没有改变,但为什么在更改 ChildrenA 时 ChildrenB 也会重新渲染
原因是在重新渲染主函数时,handleClick 函数与前一个函数不一样,我在上面的博客中解释了这一点,这就是为什么 React 注意到 props 的变化,所以它重新渲染了 ChildrenA 和 ChildrenB。
为了解决这个问题,我们将使用 useCallback
useCallback 返回一个记忆回调。
useCallback 接受一个函数和一个依赖数组,与 useEffect 相同
现在让我们在主函数中更改代码并查看日志
// main.jsx
import React, { useState, useCallback } from 'react'
import ChildrenA from '../components/ChildrenA';
import ChildrenB from '../components/ChildrenB';
import ChildrenC from '../components/ChildrenC';
const main = () => {
const [state1, setState1] = useState(0);
const [state2, setState2] = useState(0);
const handleClickA = useCallback(() => {
setState1(state1 + 1);
}, [state1])
const handleClickB = useCallback(() => {
setState2(state2 + 1);
}, [state2])
return (
<div className='flex flex-col justify-center items-center'>
<ChildrenA value={state1} handleClick={handleClickA} />
<ChildrenB value={state2} handleClick={handleClickB} />
<ChildrenC />
</div>
)
}
// what react memo do is it re-render the component only when the props change
export default React.memo(main);
现在你可以看到一切都很好👇🏻。
useMemo()
useCallback 返回一个记忆函数,类似地 useMemo 返回一个记忆值,例如,我们需要找到阶乘,并且只在数字发生变化时重新计算,而不是每次组件重新渲染时都重新计算,所以我们将使用 useCallback
import React, { useState, useMemo } from 'react'
function factorialOf(n) {
console.log('factorialOf(n) called!');
return n <= 0 ? 1 : n * factorialOf(n - 1);
}
const main = () => {
const [number, setNumber] = useState(2)
const factorial = useMemo(() => factorialOf(number), [number])
const [count, setCount] = useState(0)
return (
<div className='flex flex-col justify-center items-center'>
{factorial}
<button className='bg-gray-200 p-2 m-2' onClick={() => setNumber(number + 1)}>+</button>
{count} <button className='bg-gray-200 p-2 m-2' onClick={() => setCount(count + 1)}>+</button>
</div>
)
}
export default main;
useRef()
让控制台记录 useRef 并查看它返回什么
console.log(useRef(100));
// 这将返回类似这样的内容 👉🏻 {current: 100}
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数 (initialValue)。返回的对象将在组件的整个生命周期内持续存在。
当您在重新渲染后在 useEffect 中将普通对象与其自身进行比较时,它们是不一样的,这将触发该对象上的 useEffect,您可以运行此代码👇🏻并尝试一下。
import { useEffect, useState, useRef } from "react";
const component = () => {
const obj1 = { hi: 100 };
const obj2 = useRef({ hi: 100 });
console.log(obj1 === obj2.current);
const [state, setState] = useState(() => {
return 1;
});
useEffect(() => {
console.log('obj1 changed | ', obj1);
}, [obj1])
useEffect(() => {
console.log('obj2 changed | ', obj2.current);
}, [obj2])
return (
<div onClick={() => {
setState((value) => {
return value + 1;
});
}} className="w-screen h-screen flex justify-center items-center text-4xl font-extralight">
{state}
</div>
)
}
export default component;
你也可以使用 useState 来实现类似于 useRef 的功能
const obj = useState({current:10})[0];
🥳🥳🥳🥳🎊🎊🎊🎊 好极了!!!
您已经涵盖了所有重要的钩子。
在 Twitter 上联系我:- Twitter 🤝🏻
请查看我的 Github 来了解精彩项目:- Github 🤝🏻
在 LinkedIn 上联系我:- Linkedin 🤝🏻
阅读我的另一篇文章:使用 mongodb bcrypt 和 jwt web 令牌在 nodejs 中进行身份验证
文章来源:https://dev.to/nyctonio/all-react-hooks-and-concepts-in-a-single-post-1daf