综合技术

抛弃Redux,迎接React的hooks和context(一)

微信扫一扫,分享到朋友圈

抛弃Redux,迎接React的hooks和context(一)
0

如果你使用React很长时间,Redux应该听说过。Redux是非常酷的,它是一种获取单独组件来改变和从主应用程序store中提取数据的方法,但是它不是非常容易入手的,尤其是新手。

有很多概念性的东西,比如 reducersactionsaction creators ,并且有一些方法,比如 mapDispatchToPropsmapStateToProps ,以及需要根据常规原因创建的一堆文件和文件夹。为了分享和改编数据需要做大量的工作。

随着Context API和hooks的引入,我们可以在我们的React应用程序中重新创建Redux,而无需实际安装redux和react-redux,本篇文章将演示下如何操作。

配置

前提确保已经安装Nodejs(我使用的版本是v10.15.0),然后使用 create-react-app 初始化一个app:

npx create-react-app no-redux-app

这个根据具体网络情况需要花一些时间,确实真的要好长时间

一旦初始化结束后,在no-redux-app这个目录下通过使用命令 npm start 来启动项目,会自动打开浏览器,并且会看到类如下图的页面

ctrl + c
$ npm i react@16.8.6 react-dom@16.8.6
  • 为了保证下面的操作清晰,我们把src下面除了App.js、index.js和index.css文件的其他文件
  • 修改index.js文件
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App.jsx';
import { StoreProvider } from "./Store";

ReactDOM.render(
  <StoreProvider>
    < App / >
  </StoreProvider>
  , document.getElementById('root'));
  • 将App.js重命名为App.jsx,并用下面的代码替换
import React from 'react';

function App() {
  return (
    <React.Fragment>
      <div>
        <h1>Example</h1>
        <p>Favourite</p>
      </div>
    </React.Fragment>
  );
}

export default App;

Redux概念

根据它的文档介绍,Redux能够概括为三个基本概念: storesactionsreducers

  1. action 只有一个事情就是触发 state 的改变,它通常返回一个带有 typepayload 的对象
function actionFunc(dispatch) {
  return dispatch({type: 'COMPLETE_TODO', payload: 1}) 
}

这里的dispatch参数告诉操作该对象需要影响的store是什么,因为应用程序可以有多个reducers。这将在以后有意义。

  1. reducer 指定store将受操作影响的部分。因为redux存储是不可变的,所以reducer返回一个替换当前store的新store。Reducers通常写为switch语句。
function visibilityFilter(state, action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.payload;
    default:
      return state;
  }
}
  1. store 将所有应用程序数据保存在对象树中。Redux有一个store,但 Facebook的Flux 等其他state管理器可以拥有多个store。
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    text: 'Consider using Redux',
    completed: true,
  ]
}
  1. 应用程序中任何组件的组件都可以访问 store ,并可以通过触发 action 来更改 store

下面用React自带的hooks和context实现下这个概念

创建Store

  • 在src目录下面创建一个Store.js文件

在这里,我们将使用react context来创建一个父组件,该组件将为其子组件访问它所拥有的数据。这里暂时不深入研究context,但基本上是有provider – consumer关系。 provider 拥有所有数据, consumer 使用它(有意义)。

  • 添加下面的代码到Store.js文件中
import React from "react";

export const Store = React.createContext();

const initialState = {};

function reducer () {}

export function StoreProvider(props) {}

第3行创建了一个子组件将订阅的context对象。暂时跳过初始化state对象和reducer函数,先看下 StoreProvider

  • 添加下面的代码到 StoreProvider
export function StoreProvider(props) {
  return <Store.Provider value='data from store'>{props.children}</Store.Provider>
}
  • 现在修改index.js文件并且从./Store导入StoreProvider
import { StoreProvider } from './Store';
  • 将组件放入到中,添加完的代码看起来类似如下
ReactDOM.render(
  <StoreProvider>
    <App />
  </StoreProvider>,
  document.getElementById('root')
);
  • 在App.jsx中导入Store,代码如下
import { Store } from './Store';
  • 在App函数中加入如下代码
const store = React.useContext(Store);

这里使用了第一个hooks,就是useContext。这将使组件可以访问context提供程序的value属性中的数据。

  • <React.Fragment> 里面第一行添加 {console.log(store)}
  • 启动程序打开浏览器,在开发工具控制台中将会看到“`data from store““

如果你没有看到,请确认下你的代码是否跟我的一致

  • 文件结构

  • 代码结构
// index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { StoreProvider } from './Store';

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


// Store.js

import React from 'react';

export const Store = React.createContext();

const initialState = {};

function reducer() {}

export function StoreProvider(props) {
  return <Store.Provider value='data from store'>{props.children}</Store.Provider>;
}


// App.jsx

import React from 'react';
import { Store } from './Store';

export default function App() {
  const store = React.useContext(Store);

  return (
    <React.Fragment>
      {console.log(store)}
      <div>
        <h1>Example</h1>
        <p>Favourite</p>
      </div>
    </React.Fragment>
  );
}

创建Reducer

如果看到这里的话,说明前面的实现是你已动手实现了下,并且是没有问题的。下面继续更新代码

  • Store.js文件中,在initialState中加入下面的代码
const initialState = {
  films: [],
  favourites: [],
};

这是我们的初始 store 在添加任何新数据之前的样子。

  • 修改reducer函数看起来像这样
function reducer (state, action) {
  switch(action.type) {
    case 'FETCH_DATA':
      return { ...state, films: action.payload };
    default:
      return state;
  }
}

前面看到的reducer函数有两个参数,state – 运行时store中的数据,以及action – 返回的action对象。目前,我们的reducer有一个case ‘FETCH_DATA’,它将用传回的数据替换我们的films数组。如果调用了无效操作,则需要使用 default 关键字返回状态。

  • 在StoreProvider函数中,添加下面的代码
const [state, dispatch] = React.useReducer(reducer, initialState);
const value = { state, dispatch };

这里遇到了第二个hook,就是 useReducer 。它需要两个参数 reducerinitialState 。它返回一个数组,里面分别是state – store里面的数据和dispatch – 我们如何向reducer发送动作(反过来改变我们的state)。我希望这是有意义的。

然后,我们将新状态转换为对象,并将其分配给名为value的变量。基本上价值是相同的

const value = {
  state: state,
  dispatch: dispatch
}

但是可以在Javascript ES6及更高版本中编写更短的内容。

  • 在Store.Provider中用下面的代码替换 value='data from store'
value={value}

现在我们可以将state和dispatch传递给子组件。

  • 修改App.jsx文件,将 const store = React.useContext(Store) 替换为 const { state, dispatch } = React.useContext(Store);

现在更新控制台日志中的store以查看状态并查看控制台。

你应该看到它从Store.jsx中提取我们的initialState数据。现在让我们来处理一些数据。

创建Action

我们的redux概念的最后一块。

  • 修改App.jsx文件,再返回组件之前添加一个匿名函数,叫fetchDataAction
const fetchDataAction = async () => {}

我们将使用 fetch api 使用async/await从 tvmaze api获取数据。

  • 添加新代码到我们的新fetchDataAction函数中
const fetchDataAction = async () => {
  const data = await fetch('https://api.tvmaze.com/singlesearch/shows?q=rick-&-morty&embed=episodes');

  const dataJson = await data.json();

  return dispatch({
    type: 'FETCH_DATA',
    payload: dataJson._embedded.episodes
  });
}

我建议您在浏览器中访问api url并查看数据。episodes列表在_embedded之下。

我们返回dispatch方法,其type和payload的对象作为属性,以便我们的reducer将知道要执行的是什么情况。

我们希望每次页面加载时都运行 fetchDataAction ,所以让我们将它放在返回组件的上方的 useEffect hook中。

React.useEffect(() => {
  state.episodes.length === 0 && fetchDataAction();
});

上面的代码类似于componentDidMount。基本上应用程序加载,如果state.episodes为空(默认情况下),则运行fetchDataAction。

保存并刷新页面。查看开发工具控制台,您应该看到一些数据。

简而言之,这就是redux模式。某些情况触发了一个动作(在我们的例子中它是一个页面加载),动作在reducer中运行一个case,它反过来更新了store。现在让我们使用这些数据。

  • 修改App.jsx,在 <p>Favourite</p> 下面加入如下代码
<section>
          {
            state.films.map(f => {
              return (
                <section key={f.id}>
                  <img
                    src={f.image ? f.image.medium : ''} 
                    alt={`Year and Date ${f.name}`}
                  />
                  <div>
                    {f.name}
                  </div>
                  <section>
                    <div>
                      Season: {f.season} Number: {f.number}
                    </div>
                  </section>
                </section>
              )
            })
          }
        </section>

这段代码基本上遍历我们的剧集数组中的对象(在用api填充数据之后),并用这些数据填充dom。随意添加或删除您选择的数据点。

参考自: https://medium.com/octopus-labs-london/replacing-redux-with-react-hooks-and-context-part-1-11b72ffdb533

阅读原文...


GoWhich

广东警方与滴滴携手推出语音播报,共同保护市民出行安全

上一篇

Container Factory Microservice Python Docker Gcloud

下一篇

您也可能喜欢

评论已经被关闭。

插入图片
抛弃Redux,迎接React的hooks和context(一)

长按储存图像,分享给朋友