Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Javascript】使用react hooks 构建 redux 进行状态管理 #45

Open
AwesomeDevin opened this issue Apr 14, 2021 · 0 comments
Open

Comments

@AwesomeDevin
Copy link
Owner

前言

我们在做一个大型的复杂应用时,往往有很多数据会在多个页面多个组件中同时被使用,这时如果仍然使用props传参的方式,就会显得组件之间耦合度过高,且开发效率低

为了解决这个问题,2014年 Facebook 提出了 Flux 架构的概念,引发了很多的实现。2015年,Redux 出现,将 Flux 与函数式编程结合一起,很短时间内就成为了最热门的前端架构。

目录

  • React Hooks
  • Hooks API
  • Hooks 构建 Redux
  • 完整DEMO

React Hooks

hooks 是react 16.8 引入的特性,他允许你在不写class的情况下操作state 和react的其他特性。
hooks 只是多了一种写组件的方法,使编写一个组件更简单更方便,同时可以自定义hook把公共的逻辑提取出来,让逻辑在多个组件之间共享。

Hooks API

基础Hooks

  • useState - 声明state变量
const { state, setState } = useState(initialState)
  • useEffect - Effect钩子允许你在函数组件中执行副作用
useEffect(didUpdate,[])   //didUpdate为要做的更新,[]为要监听的变量
  • useContext - 接收一个Context对象(React.createContext返回的值)并且返回当前<MyContext.Provider>的值, 返回值取决于最近的 <MyContext.Provider>
const  store = useContext(Context)

附加Hooks

  • useReducer - 接收一个(state, action) => newState类型的reducer,并且返回一个与dispatch方法配对的state对象(如果你熟悉redux,那么你已经会了)
const [state,dispatch] = useReducer(reducers,initialState)
  • useCallback - 类似userEffect,与前者相比不能执行副作用,返回缓存的函数
  • useMemo - 类似userEffect,与前者相比不能执行副作用,返回缓存的变量
  • useRef - 类似vue中的$refs,用于操作dom
  • useImperativeHandle - 因为函数式组件不存在组件实例,所以正常情况下ref无法调用子组件的函数,useImperativeHandle返回了一个回调函数帮助我们解决此类问题
function Com(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    }
  }));
  return <input ref={inputRef} ... />;
}
Com = forwardRef(Com);
  • useLayoutEffect - 类似componentDidMount/Update, componentWillUnmount,会在dom元素更新后立即执行回调函数
  • useDebugValue - 用于自定义Hooks

Hooks 构建 Redux

Redux 规定,将模型的更新逻辑全部集中于一个特定的层(Flux 里的 store,Redux 里的 reducer)。Flux 和 Redux 都不允许程序直接修改数据,而是用一个叫作 “action” 的普通对象来对更改进行描述。

需要用到的API有createContext,useContext,useReducer

step1 - 首先,我们创建state

state.js

export default  {
  username:''
}

step2 - Redux 规定不允许程序直接修改数据,而是用一个叫作 action 的普通对象来对state进行更改

action.js

export  default {
  setUserName:function(payload){
    return {
      type:'setUserName',
      payload
    }
  }
}

step3 - 将模型的更新逻辑全部集中于一个特定的层(Flux 里的 store,Redux 里的 reducer

reducer.js

export default function reducer(state,action){
  const { payload } = action
  switch(action.type)
  {
    case 'setUserName':     // 匹配action中的type,return新的state
      return {
        ...state, 
        username: payload
      }
    default:
      return state
  }
}

step4 - 使用useReducer获取state以及用于事件派发更新state的dispatch函数

index.js

const [state,dispatch] = useReducer(reducers,initialState)

step5 - 允许所有子组件访问state及dispatch

这里我们需要排除props传参的方式,因为这样就失去了使用redux进行状态管理的意义,这里就需要用到Context
index.js

import React,{createContext, useReducer} from 'react'
import reducers from './reducers'
import initialState from './state'

export const Context = createContext({})
export const Consumer =  Context.Consumer
export function Provider(props){
  const [state,dispatch] = useReducer(reducers,initialState)
  const store = { state, dispatch } 
  return (
  <Context.Provider value={ store }>
    {props.children}
  </Context.Provider>
  )
}

这里将useReducer的返回值放到Provider的value中,子组件通过ConsumeruseContext就能访问到了

整个Redux由state、action、reducer、index构成

完整DEMO

1. index.js - 将组件放置在中

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App';
import { Provider } from './redux'

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

2. app.js - 将组件放置在中,并更新state

import React, { useContext } from 'react';
import { AppContext } from './redux'
import Child from './child'
import actions from './redux/actions'

function App() {
  const { dispatch } = useContext( AppContext )   // 获取事件派发函数
  function handleInput(e){
    dispatch(actions.setUserName(e.target.value))   // 派发更新事件并传值
  }
  return (
    <div className="App">
        <input onInput={handleInput} type='text'  />
        <Child  />
    </div>
  );
}
export default App;

3. child.js - 获取Redux中的username

import React, { useContext } from 'react';
import { AppContext } from './redux'

export default function Children(){
  const { state } = useContext(AppContext)
  return (
    <div>Name:{state.username}</div>
  )
}
  1. DEMO源码
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant