实现一个计数器案例
思路:在页面中显示一个数值,数值初始是0,会有两个按钮,加和减,通过按钮加减数值,使用redux实现
- 创建store对象
- 通过reducer存放store,是一个函数的形式,名称可以任意
- 以一个对象形式设置默认状态值,initialState
- 定义action,对象的形式
- 获取按钮,增加事件
- 在事件中触发action,通过store.dispatch方法
- 订阅store,store发生变化,内部函数执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="plus">+</button>
<span id="count">0</span>
<button id="minus">-</button>
<script src="redux.min.js"></script>
<script>
// 3. 存储默认状态
var initialState = {
count: 0
}
// 2. 创建 reducer 函数
function reducer (state = initialState, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1};
case 'decrement':
return {
count: state.count - 1}
default:
return state;
}
}
// 1. 创建 store 对象
var store = Redux.createStore(reducer);
// 4. 定义 action
var increment = {
type: 'increment' };
var decrement = {
type: 'decrement' };
// 5. 获取按钮 给按钮添加点击事件
document.getElementById('plus').onclick = function () {
// 6. 触发action
store.dispatch(increment);
}
document.getElementById('minus').onclick = function () {
// 6. 触发action
store.dispatch(decrement);
}
// 7. 订阅 store
store.subscribe(() => {
// 获取store对象中存储的状态
// console.log(store.getState());
document.getElementById('count').innerHTML = store.getState().count;
})
</script>
</body>
</html>
在React 中组件通信的数据流是单向的, 顶层组件可以通过props 属性向下层组件传递数据, 而下层
组件不能向上层组件传递数据, 要实现下层组件修改数据, 需要上层组件传递修改数据的方法到下层
组生当项目越来越大的时候, 组件之间传递数据变得越来越困难.
使用Redux管理书籍,由于Store独立于组件,使得书籍管理独立于组件,解决了组件与组件之间传递数据困难的问题
下载redux
npm install redux react-redux
redux是相关包,react-redux是react与redux更好的结合在一块
- 创建项目 create-react-app react-redux-guide
- 下载redux npm install redux react-redux
- npm start 启动项目
- 删除无用的文件,只留app.js,index.js
- 引用redux的createStore,创建store容器
- 创建reducer,initialState初始值
- 创建组件,Counter放置加减按钮及默认值,加点击事件store.dispatch
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {
createStore } from "redux";
const initialState = {
count: 0
}
function reducer(state = initialState, action) {
switch(action.type) {
case INCREMENT:
return {
...state,
count: state.count + action.payload
}
case DECREMENT:
return {
...state,
count: state.count - action.payload
}
default:
return state;
}
}
const store = createStore(reducer);
const increment = payload => ({
type: 'INCREMENT'});
const decrement = payload => ({
type: 'DECREMENT'});
store.subscribe(()=>{
console.log(store.getState())
})
function Counter () {
return <div>
<button onClick={
() => increment_async(20)}>+</button>
<span>{
count}</span>
<button onClick={
() => decrement(5)}>-</button>
</div>
}
ReactDOM.render(
// 通过provider组件 将 store 放在了全局的组件可以够的到的地方
<Counter />,
document.getElementById('root')
);
代码过于冗余,进行代码拆分
新建components文件夹放置组件,Counter.js
使用react-redux的Provider组件,connect方法
Provider组件将数据store放置全局的地方,可以用其包裹跟组件
connect方法订阅store,帮助重新渲染组件,可以组合props,dispatch方法,可以在组件中使用,在导出组件使用使用,需要在定义个方法拿到props
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {
Provider } from 'react-redux';
import {
store } from './store';
ReactDOM.render(
// 通过provider组件 将 store 放在了全局的组件可以够的到的地方
<Provider store={
store}><App/></Provider>,
document.getElementById('root')
);
/*
react-redux
Provider
connect
*/
Counter.js
import React from 'react';
import {
connect } from 'react-redux';
function Counter (props) {
return <div>
<button onClick={
() => props.dispatch({
"increment"})}>+</button>
<span>{
props.count}</span>
<button onClick={
() => props.dispatch({
"decrement"})}>-</button>
</div>
}
// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法
const mapStateToProps = state => ({
count: state.counter.count
});
export default connect(mapStateToProps)(Counter);
利用connect方法的第二个参数修改触发方法的方式,增加可读性,bindActionCreators进行简化操作
在store下创建actions、counter.action.js
import {
INCREMENT, DECREMENT, INCREMENT_ASYNC } from "../const/counter.const";
export const increment = payload => ({
type: INCREMENT, payload});
export const decrement = payload => ({
type: DECREMENT, payload});
export const increment_async = payload => ({
type: INCREMENT_ASYNC, payload});
import React from 'react';
import {
connect } from 'react-redux';
import {
bindActionCreators } from 'redux';
import * as couterActions from '../store/actions/counter.actions';
function Counter ({
count, increment, decrement, increment_async}) {
return <div>
<button onClick={
() => increment_async(20)}>+</button>
<span>{
count}</span>
<button onClick={
() => decrement(5)}>-</button>
</div>
}
// 1. connect 方法会帮助我们订阅store 当store中的状态发生更改的时候 会帮助我们重新渲染组件
// 2. connect 方法可以让我们获取store中的状态 将状态通过组件的props属性映射给组件
// 3. connect 方法可以让我们获取 dispatch 方法
const mapStateToProps = state => ({
count: state.counter.count
});
const mapDispatchToProps = dispatch => bindActionCreators(couterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
拆分reducer,store下index.js,store/reducer/counter.reducer.js
import {
INCREMENT, DECREMENT } from "../const/counter.const";
const initialState = {
count: 0
}
export default (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return {
...state,
count: state.count + action.payload
}
case DECREMENT:
return {
...state,
count: state.count - action.payload
}
default:
return state;
}
}
index.js
import {
createStore } from "redux";
export const store = createStore(reducer);
定义action,store下actions/counter使用的常量
export const INCREMENT = 'increment';
export const DECREMENT = 'decrement';
export const INCREMENT_ASYNC = 'increment_async';
进行扩展,action传递参数,在调用时传入,action携带payload参数
通过按钮控制弹出框的显隐
定义modal.js
import React from 'react';
import {
connect } from 'react-redux';
import {
bindActionCreators } from 'redux';
import * as modalActions from '../store/actions/modal.actions';
function Modal ({
showStatus, show, hide, show_async}) {
const styles = {
width: 200,
height: 200,
position: 'absolute',
left: '50%',
top: '50%',
marginLeft: -100,
marginTop: -100,
backgroundColor: 'skyblue',
display: showStatus ? 'block': 'none'
}
return <div>
<button onClick={
show_async}>显示</button>
<button onClick={
hide}>隐藏</button>
<div style={
styles}></div>
</div>
}
const mapStateToProps = state => ({
showStatus: state.modal.show
});
const mapDispatchToProps = dispatch => bindActionCreators(modalActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Modal);
App.js引用
import React from 'react';
import Counter from './components/Counter';
import Modal from './components/modal';
function App() {
return <div>
<Counter/>
<Modal />
</div>
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {
Provider } from 'react-redux';
import {
store } from './store';
ReactDOM.render(
// 通过provider组件 将 store 放在了全局的组件可以够的到的地方
<Provider store={
store}><App/></Provider>,
document.getElementById('root')
);
/*
react-redux
Provider
connect
*/
定义reducer,modal.reducer.js
import {
SHOWMODAL, HIDEMODAL } from "../const/modal.const";
const initialState = {
show: false
}
export default (state = initialState, action) => {
switch(action.type) {
case SHOWMODAL:
return {
...state,
show: true
}
case HIDEMODAL:
return {
...state,
show: false
}
default:
return state;
}
}
const下定义modal的常量
export const SHOWMODAL = 'showModal';
export const HIDEMODAL = 'hideModal';
export const SHOWMODAL_ASYNC = 'showModal_async';
modal.action.js
import {
SHOWMODAL, HIDEMODAL, SHOWMODAL_ASYNC } from "../const/modal.const";
export const show = () => ({
type: SHOWMODAL});
export const hide = () => ({
type: HIDEMODAL});
export const show_async = () => ({
type: SHOWMODAL_ASYNC});