Hooks中的useReducer

1,205 阅读2分钟

useReducer

useReducer是useState的复杂版,用来践行Flux/Redux的思想
创建代码步骤如下:

  • 1、创建初始化值initialState
  • 2、创建所有操作reducer(state,action)
  • 3、传给useReducer,得到读和写API
  • 4、调用写{{type:'操作类型'}}

我们来看一下具体代码

import React from 'react'
const initial = {
    n:0
}
const reducer = (state,action)=>{
    if(action.type === 'add'){
        return {n:state.n+action.number}
    }else if(action.type === 'multi'){
        return {n:state.n*2}
    }else{
        throw new Error('unkown type')
    }
}
export default function App(){
    const [state,dispatch] = React.useReducer(reducer,initial)
    const {n} = state
    const onClick = () => {
        dispatch({type:'add',number: 1})
    }
    const onClick2 = () =>{
        dispatch({type:'add',number: 2})
    }
    return <div className="App">
        <h1>n:{n}</h1>
        <button onClick={onClick}>+1</button>
        <button onClick={onClick2}>+2</button>
    </div>
}

渲染结果为: 可以进行对n进行+1和+2操作

react2.gif

如何代替Redux

步骤如下:

  • 1、将数据集中在一个store对象
  • 2、将所有操作集中在reducer*
  • 3、创建一个Context
  • 4、创建对数据的读写API
  • 5、将第四步的内容放到第三步的Context
  • 6、用Context.Provider将Context提供给所有组件
  • 7、各个组件用useContext获取读写API

我们通过例子来说明,

const store = {
  user: null,
  books: null,
  movies: null
};

function reducer(state, action) {
  switch (action.type) {
    case "setUser":
      return { ...state, user: action.user };
    case "setBooks":
      return { ...state, books: action.books };
    case "setMovies":
      return { ...state, movies: action.movies };
    default:
      throw new Error();
  }
}

const Context = React.createContext(null);

export default function App() {
  const [state, dispatch] = useReducer(reducer, store);

  const api = { state, dispatch };
  return (
    <Context.Provider value={api}>
      <User />
      <hr />
      <Books />
      <Movies />
    </Context.Provider>
  );
}

function User() {
  const { state, dispatch } = useContext(Context);
  useEffect(() => {
    ajax("/user").then(user => {
      dispatch({ type: "setUser", user: user });
    });
  }, []);
  return (
    <div>
      <h1>个人信息</h1>
      <div>name: {state.user ? state.user.name : ""}</div>
    </div>
  );
}

function Books() {
  const { state, dispatch } = useContext(Context);
  useEffect(() => {
    ajax("/books").then(books => {
      dispatch({ type: "setBooks", books: books });
    });
  }, []);
  return (
    <div>
      <h1>我的书籍</h1>
      <ol>
        {state.books ? state.books.map(book => <li key={book.id}>{book.name}</li>) : "加载中"}
      </ol>
    </div>
  );
}

function Movies() {
  const { state, dispatch } = useContext(Context);
  useEffect(() => {
    ajax("/movies").then(movies => {
      dispatch({ type: "setMovies", movies: movies });
    });
  }, []);
  return (
    <div>
      <h1>我的电影</h1>
      <ol>
        {state.movies
          ? state.movies.map(movie => <li key={movie.id}>{movie.name}</li>)
          : "加载中"}
      </ol>
    </div>
  );
}

// 帮助函数

// 假 ajax
// 两秒钟后,根据 path 返回一个对象,必定成功不会失败
function ajax(path) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (path === "/user") {
        resolve({
          id: 1,
          name: ""
        });
      } else if (path === "/books") {
        resolve([
          {
            id: 1,
            name: "JavaScript 高级程序设计"
          },
          {
            id: 2,
            name: "JavaScript 精粹"
          }
        ]);
      } else if (path === "/movies") {
        resolve([
          {
            id: 1,
            name: "爱在黎明破晓前"
          },
          {
            id: 2,
            name: "恋恋笔记本"
          }
        ]);
      }
    }, 2000);
  });
}

数据在两秒后加载出来:

react2.gif