内容简介:如果你使用React很长时间,Redux应该听说过。Redux是非常酷的,它是一种获取单独组件来改变和从主应用程序store中提取数据的方法,但是它不是非常容易入手的,尤其是新手。有很多概念性的东西,比如随着Context API和hooks的引入,我们可以在我们的React应用程序中重新创建Redux,而无需实际安装redux和react-redux,本篇文章将演示下如何操作。
如果你使用React很长时间,Redux应该听说过。Redux是非常酷的,它是一种获取单独组件来改变和从主应用程序store中提取数据的方法,但是它不是非常容易入手的,尤其是新手。
有很多概念性的东西,比如 reducers
, actions
, action creators
,并且有一些方法,比如 mapDispatchToProps
和 mapStateToProps
,以及需要根据常规原因创建的一堆文件和文件夹。为了分享和改编数据需要做大量的工作。
随着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能够概括为三个基本概念: stores , actions 和 reducers
- action 只有一个事情就是触发
state
的改变,它通常返回一个带有 type 和 payload 的对象
function actionFunc(dispatch) { return dispatch({type: 'COMPLETE_TODO', payload: 1}) }
这里的dispatch参数告诉操作该对象需要影响的store是什么,因为应用程序可以有多个reducers。这将在以后有意义。
- 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; } }
- store 将所有应用程序数据保存在对象树中。Redux有一个store,但 Facebook的Flux 等其他state管理器可以拥有多个store。
{ visibilityFilter: 'SHOW_ALL', todos: [ text: 'Consider using Redux', completed: true, ] }
- 应用程序中任何组件的组件都可以访问 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 。它需要两个参数 reducer 和 initialState 。它返回一个数组,里面分别是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。随意添加或删除您选择的数据点。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 抛弃Redux,迎接React的hooks和context(二)
- TLS握手:回顾1.2、迎接1.3
- 告别 2019,迎接 2020:读书、旅行、感情、工作、慈善……
- 智慧停车风景无限好,市场期许中迎接爆发
- ThinkPHP V5.0.14 版本发布——迎接新年
- ThinkPHP V5.0.14 版本发布——迎接新年
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。