反応してReduxの使用の過程において、Reduxの状態に配置する必要がある問題があった状態は、さらに、カオス状態管理の問題をもたらすことができる付加Reduxの不合理な使用では、アセンブリ内の局所的な状態で保存する必要がありますパーシャル状態の点で局所的な状態のために、フックが一つの代替よりも優れSETSTATEのクラスを設けて反応します。本論文では、状態の管理から始めて、エレガントな状態管理にフックを使用する方法について話しています。
Reduxの使い方
ローカルな状態管理のフックを反応させます
コンポーネント間の通信を解決するためのフックを反応させる方法
私のブログでオリジナル:github.com/fortheallli ...購読
まず、どのようにReduxのを使用するには
私たちは、最初にクリアする必要があり、なぜ利用Reduxの、使用Reduxのは、それは確かに合理的に自然の再来で初見の開発過程でReduxの使用できない理由がわからない場合には、重要です。
管理ツールとしてReduxの状態は、主にコンポーネント間の通信の問題を解決します。
それはコンポーネント間の通信に問題があるため、その後、明らかに全てのページの状態がReduxの中に配置され、複雑さが非常に高く、不合理です。
(1)全額Reduxの
また、Reduxの上のページレベルのルーティング、スプリット、すべては、ページがmapStateとmapDispatchの反応-再来によって達成され、任意の状態を変更するどのような状態、プレス関係なく、アプリケーションでは、問題のこれは早く行われません。
Reduxの状態がビューに状態フィードバックから更新され、プロセスが減速を行くには、長鎖、派遣からアクション、および他のロジックであって、完全なリンクです:
アクションを作成し、Reduxのミドルウェアを作成し、その上mapStateとmapDispatchを作成し、減速機能の適切なタイプを作成します。
すべての状態が再来に保存されている場合、各状態は、このプロセスにいくつかの手順を取り、面倒必要があり、コードの量を増やすは間違いありません
不合理ローカル状態の混合物(2)還元及び状態Reduxの
全量使用redux的复杂度很高,我们当然考虑将一部分状态放在redux中,一部分状态放在local state中,但是这种情况下,很容易产生一个问题,就是如果local State跟redux中的state存在状态依赖。
举例来说,在redux中的状态中有10个学生
//redux students = [{name:"小白",score:70},{name:"小红",score:50}....] 复制代码
在local state中我们保存了分数在60分以上的学生
// local state state = [{name:"小白",score:70}] 复制代码
如果redux中的学生改变了,我们需要从redux中动态的获取students信息,然后改变局部的state.结合react-redux,我们需要在容器组件中使用componentWillReceivedProps或者getDerivedStateFromProps这个声明周期,来根据props改变局部的local state.
componentWillReceivedProps这里不讨论,为了更高的安全性,在react中用静态的getDerivedStateFromProps代替了componentWillReceivedProps这里不讨论,而getDerivedStateFromProps这个声明周期函数在props和state变化的时候都会去执行,因此如果我们需要仅仅在props的改变而改变局部的local state,在这个声明周期中会存在着很复杂的判断逻辑。
redux中的状态和local state中的状态相关联的越多,getDerivedStateFromProps这个声明周期函数就越复杂
给我们的启示就是尽可能的减少getDerivedStateFromProps的使用,如果实在是redux和local state有关联性,用id会比直接用对象或者数组好,比如上述的例子,我们可以将学生分组,并给一个组号,每次在redux中的学生信息发生改变的时候会改变相应的组号。 这样在getDerivedStateFromProps只需要判断组号是否改变即可:
class Container extends React.Component{ state = { group_id:number } static getDerivedStateFromProps(props,state){ if(props.group_id!==state.group_id){ ... 更新及格的学生 }else{ return null } } } 复制代码
这里推荐https://github.com/paularmstrong/normalizr,如果实在redux和local state关联性强,可以先将数据范式化,范式化后的数据类似于给一个复杂结构一个id,这样子会简化getDerivedStateFromProps的逻辑.
(3)本节小结
如何使用redux,必须从redux的本质出发,redux的本质是为了解决组件间的通信问题,因此组件内部独有的状态不应该放在redux中,此外如果redux结合class类组件使用,可以将数据范式化,简化复杂的判断逻辑。
二、react hooks管理local state
前面将了应该如何使用redux,那么如何维护local state呢,React16.8中正式增加了hooks。通过hooks管理local state,简单易用可扩展。
在hooks中的局部状态常见的有3种,分别是useState、useRef和useReducer
(1) useState
useState是hooks中最常见的局部状态,比如:
const [hide, setHide] = React.useState(false); const [name, setName] = React.useState('BI'); 复制代码
理解useState必须明确,在react hooks中:
每一次渲染都有它自己的 Props and State
一个经典的例子就是:
function Counter() { const [count, setCount] = useState(0); function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + count); }, 3000); } return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> <button onClick={handleAlertClick}> Show alert </button> </div> ); } 复制代码
如果我按照下面的步骤去操作:
点击增加counter到3
点击一下 “Show alert”
点击增加 counter到5并且在定时器回调触发前完成
猜猜看会alert出什么?
结果是弹出了3,alert会“捕获”我点击按钮时候的状态,也就是说每一次的渲染都会有独立的props和state.
(2) useRef
在react hooks中,我们知道了每一次的渲染都会有独立的props和state,那么如果我们需要跟类组件一样,每次都能拿到最新的渲染值时,应该怎么做呢?此时我们可以用useRef
useRef提供了一个Mutable可变的数据
我们来修改上述的例子,来是的alert为5:
function Counter() { const [count, setCount] = useState(0) const late = useRef(0) function handleAlertClick() { setTimeout(() => { alert('You clicked on: ' + late.current) }, 3000) } useEffect(() => { late.current = count }) return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me</button> <button onClick={handleAlertClick}>Show alert</button> </div> ) } 复制代码
如此修改以后就不是alert3 而是弹出5
(3) useReducer
react hooks中也提供了useReducer来管理局部状态.
当你想更新一个状态,并且这个状态更新依赖于另一个状态的值时,你可能需要用useReducer去替换它们。
同样的用例子来说明:
function Counter() { const [state, dispatch] = useReducer(reducer, initialState); const { count, step } = state; useEffect(() => { const id = setInterval(() => { dispatch({ type: 'tick' }); }, 1000); return () => clearInterval(id); }, [dispatch]); return ( <> <h1>{count}</h1> <input value={step} onChange={e => { dispatch({ type: 'step', step: Number(e.target.value) }); }} /> </> ); } const initialState = { count: 0, step: 1, }; function reducer(state, action) { const { count, step } = state; if (action.type === 'tick') { return { count: count + step, step }; } else if (action.type === 'step') { return { count, step: action.step }; } else { throw new Error(); } } 复制代码
解释上面的结果主要来看useEffect部分:
useEffect(() => { const id = setInterval(() => { dispatch({ type: 'tick' }); }, 1000); return () => clearInterval(id); }, [dispatch]); 复制代码
在state中的count依赖与step,但是使用了useReducer后,我们不需要在useEffect的依赖变动数组中使用step,转而用dispatch来替代,这样的好处就是减少不必要的渲染行为.
此外:局部状态不推荐使用 useReducer ,会导致函数内部状态过于复杂,难以阅读。 useReducer 建议在多组件间通信时,结合 useContext 一起使用。
三、react hooks如何解决组件间的通信
react hooks中的局部状态管理相比于类组件而言更加简介,那么如果我们组件采用react hooks,那么如何解决组件间的通信问题。
###(1) UseContext
最基础的想法可能就是通过useContext来解决组件间的通信问题。
比如:
function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } }let Counter = createContext(null)function CounterDisplay() { let counter = useContext(Counter) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) }function App() { let counter = useCounter() return ( <Counter.Provider value={counter}> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } 复制代码
在这个例子中通过createContext和useContext,可以在App的子组件CounterDisplay中使用context,从而实现一定意义上的组件通信。
此外,在useContext的基础上,为了其整体性,业界也有几个比较简单的封装:
github.com/jamiebuilds… github.com/diegohaz/co…
但是其本质都没有解决一个问题:
如果context太多,那么如何维护这些context
也就是说在大量组件通信的场景下,用context进行组件通信代码的可读性很差。这个类组件的场景一致,context不是一个新的东西,虽然用了useContext减少了context的使用复杂度。
###(2) Redux结合hooks来实现组件间的通信
hooks组件间的通信,同样可以使用redux来实现。也就是说:
在React hooks中,redux也有其存在的意义
在hooks中存在一个问题,因为不存在类似于react-redux中connect这个高阶组件,来传递mapState和mapDispatch, 解决的方式是通过redux-react-hook或者react-redux的7.1 hooks版本来使用。
redux-react-hook
在redux-react-hook中提供了StoreContext、useDispatch和useMappedState来操作redux中的store,比如定义mapState和mapDispatch的方式为:
import {StoreContext} from 'redux-react-hook'; ReactDOM.render( <StoreContext.Provider value={store}> <App /> </StoreContext.Provider>, document.getElementById('root'), ); import {useDispatch, useMappedState} from 'redux-react-hook'; export function DeleteButton({index}) { // Declare your memoized mapState function const mapState = useCallback( state => ({ canDelete: state.todos[index].canDelete, name: state.todos[index].name, }), [index], ); // Get data from and subscribe to the store const {canDelete, name} = useMappedState(mapState); // Create actions const dispatch = useDispatch(); const deleteTodo = useCallback( () => dispatch({ type: 'delete todo', index, }), [index], ); return ( <button disabled={!canDelete} onClick={deleteTodo}> Delete {name} </button> ); } 复制代码
react-redux 7.1的hooks版
这也是官方较为推荐的,react-redux 的hooks版本提供了useSelector()、useDispatch()、useStore()这3个主要方法,分别对应与mapState、mapDispatch以及直接拿到redux中store的实例.
简单介绍一下useSelector,在useSelector中除了能从store中拿到state以外,还支持深度比较的功能,如果相应的state前后没有改变,就不会去重新的计算.
举例来说,最基础的用法:
import React from 'react'import { useSelector } from 'react-redux'export const TodoListItem = props => { const todo = useSelector(state => state.todos[props.id]) return <div>{todo.text}</div>} 复制代码
实现缓存功能的用法:
import React from 'react'import { useSelector } from 'react-redux'import { createSelector } from 'reselect'const selectNumOfDoneTodos = createSelector( state => state.todos, todos => todos.filter(todo => todo.isDone).length )export const DoneTodosCounter = () => { const NumOfDoneTodos = useSelector(selectNumOfDoneTodos) return <div>{NumOfDoneTodos}</div>}export const App = () => { return ( <> <span>Number of done todos:</span> <DoneTodosCounter /> </> ) } 复制代码
在上述的缓存用法中,只要todos.filter(todo => todo.isDone).length不改变,就不会去重新计算.
四、总结
地元の部分の状態の管理と制御は非常に便利なレンダリングが、それでも基本的に構成層のビューにフックを反応させ、そして完璧ながないように、ローカルおよびグローバルな状態の状態に完全な状態管理に反応し、部分的な状態を簡略化しフックを反応させますコンポーネント間の通信の問題を解決する、すなわち、それは、Reduxの及び他の状態管理部と矛盾し、フックの性質は反応しません。
私の練習では、再来とコンポーネント間の通信を実現し、コードは単純な読み取りになるように、ローカルの状態管理を実現するためにフックを反応させ、だけでなく、不要なReduxの定型コードの多くを減らします。