redux
redux
- 在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;
- 在更改数据时,需要传给dispatch一个action,action中有个type属性来表示更改的类型;
- action : 里面要有一个type属性和要更改的数据
- dispatch根据定义的type类型值,进行数据不同的处理和更改
- vuex : 必须提交commit的mutation来更改数据;
- redux中规定,redux中的state必须经过dipatch去更改;
- redux: 实现组件之间数据传递;redux准备一个全局的store;
- 在这个容器中准备一个state,专门来存储公共数据;如果外界想访问这个数据,必须经过store给提供的getState这个方法来获取store中的数据;在getState 中为了防止外界直接修改,所以只能深克隆一个state返回;在redux中规定,更改store中的state的唯一方法是 派发dispatch;所以在store中还提供一个dispatch方法
- 在这个容器中准备一个state,专门来存储公共数据;如果外界想访问这个数据,必须经过store给提供的getState这个方法来获取store中的数据;在getState 中为了防止外界直接修改,所以只能深克隆一个state返回;在redux中规定,更改store中的state的唯一方法是 派发dispatch;所以在store中还提供一个dispatch方法
gitState
const CHANGE_TITLE_TEXT="CHANGE_TITLE_TEXT";
const CHANGE_CONTENT_COLOR="CHANGE_CONTENT_COLOR";
function createStore(reducer){
let state;// 私有的变量
// 专门用来获取store中的数据
// 深克隆: 把当前作用域下的state克隆一份,并且返回,那么外界拿到的是新克隆的一份,所以不能对当前作用域下的state进行更改;
let getState=()=>JSON.parse(JSON.stringify(state));
function dispatch(action){
// reducer 的返回值覆盖了当前作用域的state;
state=reducer(state,action)// 实参
}
dispatch({});// 为了把初始状态的initState赋值给store中的state;
return {
getState,
dispatch
}
}
let initState = {
title:{color:"red",text:"下午别睡觉"},
content:{color:"yellow",text:"上午也别睡觉"}
};
// 自己的写的;reducer存放了根据type类型改state的逻辑
function reducer(state=initState,action){// 形参
switch(action.type){
case CHANGE_TITLE_TEXT:
return {...state,title:{...state.title,text:action.text}};
case CHANGE_CONTENT_COLOR:CHANGE_CONTENT_COLOR:
return {...state,content:{...state.content,color:action.color}}
}
return state;
}
let store = createStore(reducer);
function renderTitle(){
let title = document.getElementById("title");
title.innerHTML = store.getState().title.text;
title.style.color=store.getState().title.color;
}
function renderContent(){
let content = document.getElementById("content");
content.innerHTML = store.getState().content.text;
content.style.color=store.getState().content.color;
}
function renderApp(){
renderTitle();
renderContent();
}
renderApp();
// 更改content的字体颜色,改成blue;
setTimeout(function(){
// 更改state中的值;但是没有更新视图;
// state.title.text="好好学习";// 在react中不允许直接更改state的值;redux 需要调用dispatch才能更改redux中的数据;
store.dispatch({type:CHANGE_TITLE_TEXT,text:"好好学习"});
store.dispatch({type:CHANGE_CONTENT_COLOR,color:"blue"})
renderApp();
},2000)
subscribe
-
订阅;可以订阅一些方法,当执行完dispatch之后,会把订阅的方法执行
-
// 订阅renderApp这个方法,然后执行dispatch,就会发布这个方法;
// let f = store.subscribe(renderApp);
// f();// 取消订阅
const CHANGE_TITLE_TEXT="CHANGE_TITLE_TEXT";
const CHANGE_CONTENT_COLOR="CHANGE_CONTENT_COLOR";
function createStore(reducer){
let state;
let listeners=[];
let getState=()=>JSON.parse(JSON.stringify(state));
function dispatch(action){
state=reducer(state,action);
// 调用dispatch就会发布listener中的方法;
listeners.forEach(item=>{
if(typeof item==="function"){
item();
}
})
}
dispatch({});
function subscribe(fn){
listeners.push(fn);
// 返回一个取消订阅的方法,当返回值执行时,取消对应的订阅
return ()=>{
listeners = listeners.filter(item=>item!==fn);
}
}
return {
getState,
dispatch,
subscribe
}
}
let initState = {
title:{color:"red",text:"下午别睡觉"},
content:{color:"yellow",text:"上午也别睡觉"}
};
function reducer(state=initState,action){// 形参
switch(action.type){
case CHANGE_TITLE_TEXT:
return {...state,title:{...state.title,text:action.text}};
case CHANGE_CONTENT_COLOR:
return {...state,content:{...state.content,color:action.color}}
}
return state;
}
let store = createStore(reducer);
function renderTitle(){
let title = document.getElementById("title");
title.innerHTML = store.getState().title.text;
title.style.color=store.getState().title.color;
}
function renderContent(){
let content = document.getElementById("content");
content.innerHTML = store.getState().content.text;
content.style.color=store.getState().content.color;
}
function renderApp(){
renderTitle();
renderContent();
}
renderApp();
setTimeout(function(){
store.dispatch({type:CHANGE_TITLE_TEXT,text:"好好学习"});
store.dispatch({type:CHANGE_CONTENT_COLOR,color:"blue"})
// renderApp();
},2000)
redux的封装
// state getState dispacth subscribe
function createStore(reducer){
let state;
let getState=()=>JSON.parse(JSON.stringify(state));
let dispacth=(action)=>{
state=reducer(state,action);
listeners.forEach(item=>{
if(typeof item==="function"){
item();
}
})
}
let listeners=[];
dispacth({});
let subscribe=(fn)=>{
listeners.push(fn);
return ()=>{
listeners=listeners.filter(item=>item!==fn);
}
}
return {
getState,
dispacth,
subscribe
}
}
let combineReducers=(reducers)=>{
return (state,action)=>{// 这个函数会传给createStore,就是createStore中的reducer方法
let obj = {};
for(let key in reducers){
obj[key]=reducers[key](state[key],action)
}
return obj;// obj会将store中的state覆盖;
}
}
export default {
createStore,
combineReducers
}
dispatch
- 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法, 视图也就不会更新;所以需要调用setState方法,执行render;
let a={num:100};
const ADD_COUNT="ADD_COUNT";
const MIN_COUNT="MIN_COUNT";
function reducer(state=a,action){
switch(action.type){
case ADD_COUNT:
return {...state,num:state.num+action.val};
case MIN_COUNT:
return {...state,num:state.num-1};
}
return state;// createStore中的state就被赋予了默认值;
}
let store = createStore(reducer);
// 想更新视图,需要调用setState;
class Count extends React.Component{
constructor(){
super()
this.state={num:store.getState().num}// 需要将store中的数据跟当前组件的state绑定在一起;
}
componentDidMount(){
// 当调用dispatch会执行订阅的方法
// 订阅更新state的方法;
store.subscribe(()=>{
return this.setState({num:store.getState().num})
})
}
add=(val)=>{
// dispatch 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法,
// 视图也就不会更新;所以需要调用setState方法,执行render;
store.dispatch({type:ADD_COUNT,val:val})
}
min=()=>{
store.dispatch({type:MIN_COUNT})
}
render(){
return <div>
<button onClick={()=>{this.add(2)}}>+</button>
<span>{this.state.num}</span>
<button onClick={this.min}>-</button>
</div>
}
}
combineReducers 多个reducer合并成一个
function combineReducers(reducers){
return (state={},action)=>{// 返回的这个函数就是createStore中的reducer这个函数;
// 这个函数的返回值是什么?函数的结果就是给store中的state赋值;
let obj = {};
for(let key in reducers){
// 把reducer的返回值赋值给obj键值对;
// key : counter todo
obj[key]=reducers[key](state[key],action);
// obj[counter]={num:100}函数执行的结果
// obj[todo]={todo:["跑步","爬山"]}
}
return obj;
}
}
// 最后的store中的state的初始值是什么?
// 每个项目都有一个store,但是有多个组件,还想每个组件单独管理自己独立的状态;
// 把所有的组件的状态集中到一个store中;再把各自的状态单独传递给每一个组件的reducer;
// {counter:{num:100},todo:{todo:["跑步","爬山"]}}
let reducer = combineReducers({
// 前面这个是属性名,后面是属性值;属性值作为值一定要看当前的数据类型
counter:count,// counter是reducers下counter的函数
todo:todo// // reducers下todo的函数
});
export default reducer;
react-redux 将数据放到组件的props上
- 将store的公共数据放到组件的属性上;
属性和状态的更新都能引发视图的更新;
react-redux要求返回一个连接后的组件;
class Counter extends React.Component{
// constructor(){
// super();
// this.state={num:store.getState().counter.num}
// }
// componentDidMount(){
// // 在redux中,A组件通过dispatch更新了store中的数据,同时B组件也使用了store中这个数据,但是B组件不会自动更新;
// store.subscribe(()=>{
// this.setState({num:store.getState().counter.num})
// })
// }
add=()=>{
// store.dispatch(actions.add());
//当使用方法时,保持和action中的add一致;
this.props.add();
}
min=()=>{
// store.dispatch(actions.min());
this.props.min();
}
render(){
// 1. 组件事件 ==> 2. action-type==>3.actions==> 4.reducer==>5.组件
//想更新视图,需要调用render方法,那么执行setState就会调用render;
// 每当dispatch时,都要更新视图的;那么把setState方法进行订阅;
return <div>
<button onClick={this.add}>+</button>
{this.props.num}
<button onClick={this.min}>-</button>
</div>
}
}
// connect : 第一个参数: 函数 第二个参数: 是当前组件
// actions : 是一个返回类型的对象;
// mapStateToProps\mapDisPatchToProps都是在connect函数内部调用的
let mapStateToProps=state=>({...state.counter}); // 当执行connect时,会默认调用这个箭头函数,
// 并且将store中的state数据传给当前的函数state参数;返回当前组件对应的数据,
// 并且放在了当前组件的行间属性上;
let mapDisPatchToProps=(dispatch)=>{// dispatch==>store.dispatch
return {// 这个对象会放在组件的属性上;
add:()=>{dispatch(actions.add())},
min:()=>{dispatch(actions.min())}
}
}
// 都是将这两个函数的返回值放到组件的属性上;
export default connect(mapStateToProps,actions)(Counter);
//在执行connect时,判断第二个参数是否是一个函数,如果是函数,则将函数的执行结果放在组件的行间属性上,如果是一个对象,那么会默认调用一个bindActionsCreator这个方法,将该方法的返回值放在组件行间属性上当前属性传给组件
// action 就是connect传入的action dispatch是store中的dispatch方法
// let bindActionCreator=(action,dispatch)=>{
// let obj ={};
// for(let key in action){
// obj[key]= ()=>{
// dispatch(action[key]())
// }
// }
// return obj;
// }
import React from "react";
import ReactDOM from "react-dom";
import Counter from "./page/1.counter";
import Compute from "./page/2.compute.js";
// 将store注入到每一个组件;需要从react-redux解构出Provider这个组件,
// 然后将所有的组件嵌套在Provider组件里面;
import {Provider} from "react-redux";
import store from "./store/index";
ReactDOM.render(
<Provider store={store}>
<Counter></Counter>
<Compute></Compute>
</Provider>
,document.getElementById("root"));
redux模拟
index
解决react框架中组件和组件数据传递问题;
redux : 也是一个用于管理公共状态的模块;
在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,需要传给dispatch一个action,action中有个type属性来表示更改的类型;
action : 里面要有一个type属性和要更改的数据
dispatch根据定义的type类型值,进行数据不同的处理和更改
//vuex : 必须提交commit的mutation来更改数据;
redux中规定,redux中的state必须经过dipatch去更改;
<body>
<div id="title"></div>
<div id="content"></div>
<script>
// vuex 必须和vue结合使用;
// 解决react框架中组件和组件数据传递问题;
// redux : 也是一个用于管理公共状态的模块;
let state = {
title: {
color: "red",
text: "下午别睡觉"
},
content: {
color: "yellow",
text: "上午也别睡觉"
}
};
function renderTitle() {
let title = document.getElementById("title");
title.innerHTML = state.title.text;
title.style.color = state.title.color;
}
function renderContent() {
let content = document.getElementById("content");
content.innerHTML = state.content.text;
content.style.color = state.content.color;
}
function renderApp() {
renderTitle();
renderContent();
}
renderApp();
// 在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,
需要传给dispatch一个action,action中有个type属性来表示更改的类型;
// action : 里面要有一个type属性和要更改的数据
// dispatch根据定义的type类型值,进行数据不同的处理和更改
// vuex : 必须提交commit的mutation来更改数据;
// redux中规定,redux中的state必须经过dipatch去更改;
const CHANGE_TITLE_TEXT = "CHANGE_TITLE_TEXT";
const CHANGE_CONTENT_COLOR = "CHANGE_CONTENT_COLOR";
function dispatch(action) {
switch (action.type) {
case CHANGE_TITLE_TEXT:
// 对原有的state进行解构,并且让新的值放在这个对象中
state = {
...state,
title: {
...state.title,
text: action.text
}
};
break;
case CHANGE_CONTENT_COLOR:
state = {
...state,
content: {
...state.content,
color: action.color
}
}
}
}
// 更改content的字体颜色,改成blue;
setTimeout(function () {
// 更改state中的值;但是没有更新视图;
// state.title.text="好好学习";// 在react中不允许直接更改state的值;redux
需要调用dispatch才能更改redux中的数据;
dispatch({
type: CHANGE_TITLE_TEXT,
text: "好好学习"
});
dispatch({
type: CHANGE_CONTENT_COLOR,
color: "blue"
})
renderApp();
}, 2000)
</script>
</body>
</html>
index-getState
解决react框架中组件和组件数据传递问题;
redux : 也是一个用于管理公共状态的模块;
store : state getState dispatch subscribe
redux : 外界不能直接更改state中的值;
redux : 实现组件之间数据传递;redux准备一个全局的store;在这个容器中准备一个state,专门来存储公共数据;如果外界想访问这个数据,必须经过store给提供的getState这个方法来获取store中的数据;在getState 中为了防止外界直接修改,所以只能深克隆一个state返回;在redux中规定,更改store中的state的唯一方法是 派发dispatch;所以在store中还提供一个dispatch方法
在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,需要传给dispatch一个action,action中有个type属性来表示更改的类型;
action : 里面要有一个type属性和要更改的数据
dispatch根据定义的type类型值,进行数据不同的处理和更改
redux中规定,redux中的state必须经过dipatch去更改;
<body>
<div id="title"></div>
<div id="content"></div>
<script>
// vuex 必须和vue结合使用;
// 解决react框架中组件和组件数据传递问题;
// redux : 也是一个用于管理公共状态的模块;
// store : state getState dispatch subscribe
// redux : 外界不能直接更改state中的值;
// let a = {};
// let b = a;// 空间地址一样
// a.name="88";// 新增了吧
// let c = {m:{bar:100},f:{foo:200}};
// let d ={};
// for(let key in c){
// d[key]=c[key];
// }
// c.m.bar=666;
// d.m.bar// ?
// redux : 实现组件之间数据传递;redux准备一个全局的store;在这个容器中准备一个state,
专门来存储公共数据;如果外界想访问这个数据,必须经过store给提供的getState这个方法来获取store中
的数据;在getState 中为了防止外界直接修改,所以只能深克隆一个state返回;在redux中规定,
更改store中的state的唯一方法是 派发dispatch;所以在store中还提供一个dispatch方法
const CHANGE_TITLE_TEXT = "CHANGE_TITLE_TEXT";
const CHANGE_CONTENT_COLOR = "CHANGE_CONTENT_COLOR";
function createStore(reducer) {
let state; // 私有的变量
// 专门用来获取store中的数据
// 深克隆: 把当前作用域下的state克隆一份,并且返回,那么外界拿到的是新克隆的一份,
所以不能对当前作用域下的state进行更改;
let getState = () => {
return JSON.parse(JSON.stringify(state))
};
function dispatch(action) {
// reducer 的返回值覆盖了当前作用域的state;
state = reducer(state, action) // 实参
}
dispatch({}); // 为了把初始状态的initState赋值给store中的state;
return {
getState,
dispatch
}
}
let initState = {
title: {
color: "red",
text: "下午别睡觉"
},
content: {
color: "yellow",
text: "上午也别睡觉"
}
};
// 自己的写的;reducer存放了根据type类型改state的逻辑
function reducer(state = initState, action) { // 形参
switch (action.type) {
case CHANGE_TITLE_TEXT:
return {
...state, title: {
...state.title,
text: action.text
}
};
case CHANGE_CONTENT_COLOR:
CHANGE_CONTENT_COLOR:
return {
...state,
content: {
...state.content,
color: action.color
}
}
}
return state;
}
let store = createStore(reducer);
function renderTitle() {
let title = document.getElementById("title");
title.innerHTML = store.getState().title.text;
title.style.color = store.getState().title.color;
}
function renderContent() {
let content = document.getElementById("content");
content.innerHTML = store.getState().content.text;
content.style.color = store.getState().content.color;
}
function renderApp() {
renderTitle();
renderContent();
}
renderApp();
// 在redux中如果想更改数据,必须调用dispatch这个方法来更改state的数据;在更改数据时,
需要传给dispatch一个action,action中有个type属性来表示更改的类型;
// action : 里面要有一个type属性和要更改的数据
// dispatch根据定义的type类型值,进行数据不同的处理和更改
// vuex : 必须提交commit的mutation来更改数据;
// redux中规定,redux中的state必须经过dipatch去更改;
// 更改content的字体颜色,改成blue;
setTimeout(function () {
// 更改state中的值;但是没有更新视图;
// state.title.text="好好学习";// 在react中不允许直接更改state的值;redux
需要调用dispatch才能更改redux中的数据;
store.dispatch({
type: CHANGE_TITLE_TEXT,
text: "好好学习"
});
store.dispatch({
type: CHANGE_CONTENT_COLOR,
color: "blue"
})
renderApp();
}, 2000)
</script>
</body>
index-subscribe
state getState dispatch subscribe
subscribe : 订阅;可以订阅一些方法,当执行完dispatch之后,会把订阅的方法执行
<body>
<div id="title"></div>
<div id="content"></div>
<script>
// state getState dispatch subscribe
// subscribe : 订阅;可以订阅一些方法,当执行完dispatch之后,会把订阅的方法执行
const CHANGE_TITLE_TEXT="CHANGE_TITLE_TEXT";
const CHANGE_CONTENT_COLOR="CHANGE_CONTENT_COLOR";
function createStore(reducer){
let state;
let listeners=[];
let getState=()=>JSON.parse(JSON.stringify(state));
function dispatch(action){
state=reducer(state,action);
// 调用dispatch就会发布listener中的方法;
listeners.forEach(item=>{
if(typeof item==="function"){
item();
}
})
}
dispatch({});
function subscribe(fn){
listeners.push(fn);
// 返回一个取消订阅的方法,当返回值执行时,取消对应的订阅
return ()=>{
listeners = listeners.filter(item=>item!==fn);
}
}
return {
getState,
dispatch,
subscribe
}
}
let initState = {
title:{color:"red",text:"下午别睡觉"},
content:{color:"yellow",text:"上午也别睡觉"}
};
function reducer(state=initState,action){// 形参
switch(action.type){
case CHANGE_TITLE_TEXT:
return {...state,title:{...state.title,text:action.text}};
case CHANGE_CONTENT_COLOR:
return {...state,content:{...state.content,color:action.color}}
}
return state;
}
let store = createStore(reducer);
function renderTitle(){
let title = document.getElementById("title");
title.innerHTML = store.getState().title.text;
title.style.color=store.getState().title.color;
}
function renderContent(){
let content = document.getElementById("content");
content.innerHTML = store.getState().content.text;
content.style.color=store.getState().content.color;
}
function renderApp(){
renderTitle();
renderContent();
}
renderApp();
setTimeout(function(){
store.dispatch({type:CHANGE_TITLE_TEXT,text:"好好学习"});
store.dispatch({type:CHANGE_CONTENT_COLOR,color:"blue"})
// renderApp();
},2000)
// 订阅renderApp这个方法,然后执行dispatch,就会发布这个方法;
let f = store.subscribe(renderApp);
// f();// 取消订阅
</script>
</body>
</html>
redux源码
function createStore(reducer) {
let state;
let getState = () => JSON.parse(JSON.stringify(state));
// action : type 要改的数据
function dispatch(action) {
state = reducer(state, action);
listeners.forEach(item => {
if (typeof item === "function") {
item();
}
})
}
let listeners = [];// 存储订阅的事件的一个容器;当调用dispatch的时候,让这个事件池中的方法执行;
dispatch({});// 为了初始化数据
let subcribe = (fn) => {
listeners.push(fn);
return () => {
listeners = listeners.filter(item => item !== fn);
}
}
return {
getState,
dispatch,
subcribe
}
}
export default createStore;
补充
<script>
//vue->vueX
//react->
//事件池 所有公共状态
//修改状态
//reducer 管理员 登记在案 (原始状态state={},dispatch对象) return state
把原始容器中的状态修改为啥;
//getState()获得信息
//store,dispatch type
//store.subscribe 发布订阅
//redux 的漏洞
//setState的同步异步?
//index.js 工程化
//action-types.js 派发行为标识ACTION,TYPE的宏管理 变量
//reducers index.js 把各个小reducers版块合一起
//
//redux源码
function createStore(reducer) {
if (typeof reducer !== "function") {
throw new TypeError("reducer must be a function")
}
let state;
let listeners = [];
const getState = function getState() {
//防止返回状态和原始共用地址
return JSON.parse(JSON.stringify(state));
}
//向容器事件池中加方法
const subscribe = function getState(func) {
if (typeof func !== "function") {
if (listeners.includes(func)) {
listeners.push(func);
}
}
return function unsubscribe() {
listeners = listeners.filter(item => item !== func);
}
}
//派发任务
const dispatch = function getState(action) {
if (action === null || action === undefined) return;
if (typeof action !== "object") return;
if (!action.hasOwnProperty("type")) return;
//执行
state = reducer(state, action);
//
listeners.forEach(item => {
item();
})
}
//初始的时候先开发一次dispatch
// const randomString=function(){
// return Math.random().toString(36).substring(7).split("").join(".")
// }
dispatch({
type: "@@redux/INIT$(Math.random())"
})
return {
getState,
subscribe,
dispatch
}
}
export {
createStore
};
</script>
redux案例(加减)
- index.html + redux.js ==> couter.jsx
index.html
<body>
<button id="add">+</button>
<span id="text"> </span>
<button id="min">-</button>
<script src="./redux.js"></script>
<script>
let add = document.getElementById("add");
let text = document.getElementById("text");
let min = document.getElementById("min");
let initState = {
num: 666,
a: 10
};
const ADD_COUNT = "ADD_COUNT";
const MIN_COUNT = "MIN_COUNT";
// reducer :返回一个state
// 第一次执行reducer ==> 在createStore中有一行默认调用dispatch
function reducer(state = initState, action) {
// 形参赋默认值,当传递进来的实参是undefined或不传参,那么形参就代表默认值
switch (action.type) {
case ADD_COUNT:
return {
...state, num: state.num + 1
} // 这个对象会对store中的state重新赋值;
case MIN_COUNT:
return {
...state, num: state.num - 1
};
}
return state;
}
let store = createStore(reducer);
//渲染视图;刷新视图;每当dispatch一次就执行一次该渲染函数
function renderSpan() {
text.innerHTML = store.getState().num;
}
renderSpan();
// 每当执行一次dispatch,都会执行一次renderSpan
store.subscribe(renderSpan);
// 绑定点击事件
// 点击按钮更改store中的state中的num值;并且更新视图;
// 更改store数据流程
// 1. const定义type类型
// 2. dispatch派发action
add.onclick = function () {
store.dispatch({
type: ADD_COUNT
})
}
min.onclick = function () {
store.dispatch({
type: MIN_COUNT
})
}
</script>
</body>
redux.js
// state getState dispatch subscribe
// redux : 数据的公共管理;实现组件之间数据的相互调用;和vuex一样;数据传递的一种解决方案;
// 既然各个组件都能使用,存储到一个公共的位置;
function createStore(reducer){
let state;
// 外界无法访问这个state;
// 在redux中规定,只有dispatch能更改数据,通过dispatch派发的动作找到相应的更改数据的reducer;
执行reducer
let getState=()=>JSON.parse(JSON.stringify(state));
let dispatch=(action)=>{
state=reducer(state,action);
// 循环订阅的事件池;让池子的方法执行
listeners.forEach(item=>{
if(typeof item==="function"){
item();
}
//typeof(item)==="function"?item():null;
//1==1?console.log(1):console.log(2);
// let a = 1==2?89:67;
// (1==1)?100:200
// 100;
})
}
let listeners = [];
dispatch({});// 为了初始化store中的state;
let subscribe=(fn)=>{
listeners.push(fn);
return ()=>{
listeners=listeners.filter(item=>item!==fn);
}
}
return {
// 把getState这个空间地址暴露出去
getState,
dispatch,
subscribe
}
}
export default createStore;
counter.jsx
import React from "react";
import ReactDOM from "react-dom";
import createStore from "./redux";
// 1. 定义常量
// 2. 派发一个动作
// 3. 编写reducer
let a = { num: 100 };
const ADD_COUNT = "ADD_COUNT";
const MIN_COUNT = "MIN_COUNT";
function reducer(state = a, action) {
switch (action.type) {
case ADD_COUNT:
return { ...state, num: state.num + action.val };
case MIN_COUNT:
return { ...state, num: state.num - 1 };
}
return state;// createStore中的state就被赋予了默认值;
}
let store = createStore(reducer);
// 想更新视图,需要调用setState;
class Count extends React.Component {
constructor() {
super()
this.state = { num: store.getState().num }
// 需要将store中的数据跟当前组件的state绑定在一起;
}
componentDidMount() {
// 当调用dispatch会执行订阅的方法
// 订阅更新state的方法;
store.subscribe(() => {
return this.setState({ num: store.getState().num })
})
}
add = (val) => {
// dispatch 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法,
视图也就不会更新;所以需要调用setState方法,执行render;
store.dispatch({ type: ADD_COUNT, val: val })
}
min = () => {
store.dispatch({ type: MIN_COUNT })
}
render() {
return <div>
<button onClick={() => { this.add(2) }}>+</button>
<span>{this.state.num}</span>
<button onClick={this.min}>-</button>
</div>
}
}
ReactDOM.render(<Count></Count>, document.getElementById("root"));
redux工程化
- 一个组件
- 两个组件
page/counter.js
import React from "react";
import actions from "../store/actions/counter"
import store from "../store/index.js"
// 1. 定义常量
// 2. 派发一个动作
// 3. 编写reducer
// 想更新视图,需要调用setState;
export default class Count extends React.Component {
constructor() {
super()
this.state = { num: store.getState().counter.num }// 需要将store中的数据跟当前组件
的state绑定在一起;
}
componentDidMount() {
// 当调用dispatch会执行订阅的方法
// 订阅更新state的方法;
store.subscribe(() => {
return this.setState({ num: store.getState().counter.num })
})
}
add = (val) => {
// dispatch 仅仅是让store中的数据发生了变化,但是不会触发组件的render方法,视图也就不会更新;
所以需要调用setState方法,执行render;
store.dispatch(actions.add(val))
}
min = (val) => {
store.dispatch(actions.min(val))
}
render() {
return <div>
<button onClick={() => { this.add(2) }}>+</button>
<span>{this.state.num}</span>
<button onClick={this.min}>-</button>
</div>
}
}
page/todo.js
import React from "react";
export default class Todo extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div>TODO</div>
}
}
store/actions/counter.js
// actions: 这是一个动作,存放dispatch派发的参数;这个要用到常量
// 把action-type.js中的常量都导进来,放在当前页面types这个变量上
import * as types from "../action-type.js";
export default {
add(val){
// add是个函数,其返回值才是dispatch的实参;
// 因为在项目需要在action发送请求,所以需要写成函数的形式;
return {type:types.ADD_COUNT,val:val};
},
min(){
return {type:types.MIN_COUNT}
}
}
store/reducers/counter.js
// 每个组件对应一个reducer;所以需要创建一个文件夹;
// reducer 存储了当前组件的初始状态;
import * as types from "../action-type.js";
let a={num:100};
function count(state=a,action){
switch(action.type){
case types.ADD_COUNT:
return {...state,num:state.num+action.val};
case types.MIN_COUNT:
return {...state,num:state.num-1};
}
return state;// createStore中的state就被赋予了默认值;
}
export default count;
store/reducers/index.js
// 这个文件用于合并各个的reducer;
import count from "./counter.js";
import todo from "./todo.js";
// 合并多个reducer;合并成一个;
function combineReducers(reducers){
return (state={},action)=>{// 返回的这个函数就是createStore中的reducer这个函数;
// 这个函数的返回值是什么?函数的结果就是给store中的state赋值;
let obj = {};
for(let key in reducers){
// 把reducer的返回值赋值给obj键值对;
// key : counter todo
obj[key]=reducers[key](state[key],action);
// obj[counter]={num:100}函数执行的结果
// obj[todo]={todo:["跑步","爬山"]}
}
return obj;
}
}
// 最后的store中的state的初始值是什么?
// 每个项目都有一个store,但是有多个组件,还想每个组件单独管理自己独立的状态;
把所有的组件的状态集中到一个store中;再把各自的状态单独传递给每一个组件的reducer;
// {counter:{num:100},todo:{todo:["跑步","爬山"]}}
let reducer = combineReducers({
// 前面这个是属性名,后面是属性值;属性值作为值一定要看当前的数据类型
counter:count,// counter是reducers下counter的函数
todo:todo// // reducers下todo的函数
});
export default reducer;
store/reducers/todo.js
// 每一个组件都有自己对应独立的reducer
let initTodo = {
todo: ["跑步", "爬山"]
}
function todo(state = initTodo, action) {
return state;
}
export default todo;
store/action-type.js
// 这个文件夹中存储了所有的常量;
//点击新增
export const ADD_COUNT="ADD_COUNT";
// 点击减少
export const MIN_COUNT="MIN_COUNT";
// export default{
// ADD_COUNT:"ADD_COUNT",
// MIN_COUNT:"MIN_COUNT"
// }
store/index.js
// 这个文件夹中存储了所有的常量;
//点击新增
export const ADD_COUNT="ADD_COUNT";
// 点击减少
export const MIN_COUNT="MIN_COUNT";
// export default{
// ADD_COUNT:"ADD_COUNT",
// MIN_COUNT:"MIN_COUNT"
// }
index.js
import React from "react";
import ReactDOM from "react-dom";
import Todo from './page/todo.js'
import Count from "./page/counter.js"
ReactDOM.render(<div>
<Count></Count>
<Todo></Todo>
</div>, document.getElementById("root"));
redux.js
function createState(reducer) {
// 数据公共管理,实现组件之间数据的相互调用
let state;
let listenres = [];
// 在redux中规定,只有dispatch能更改数据,reducer
let getState = () => JSON.parse(JSON.stringify(state));
let dispatch = (action) => {
state = reducer(state, action);
listenres.forEach(item => typeof item === "function" ? item() : null);
}
dispatch({});
let subscribe = (fn) => {
listenres.push(fn)
return () => {
listenres = listenres.filter(item => item !== fn);
}
}
return {
getState,
dispatch,
subscribe
}
}
export default createState;
react-redux工程化
page/1.counter
import React from "react";
// import store from "../store/index.js";
import actions from "../store/actions/counter";
// react-redux: 将store的公共数据放到组件的属性上;
// 属性和状态的更新都能引发视图的更新;
// react-redux要求返回一个连接后的组件;
import {connect} from "react-redux";
// 可以将store中的数据和action的动作以属性的方式传递给该组件
class Counter extends React.Component{
// constructor(){
// super();
// this.state={num:store.getState().counter.num}
// }
// componentDidMount(){
// // 在redux中,A组件通过dispatch更新了store中的数据,同时B组件也使用了store中这个数据,
但是B组件不会自动更新;
// store.subscribe(()=>{
// this.setState({num:store.getState().counter.num})
// })
// }
add=()=>{
// store.dispatch(actions.add());
//当使用方法时,保持和action中的add一致;
this.props.add();
}
min=()=>{
// store.dispatch(actions.min());
this.props.min();
}
render(){
// 1. 组件事件 ==> 2. action-type==>3.actions==> 4.reducer==>5.组件
//想更新视图,需要调用render方法,那么执行setState就会调用render;
// 每当dispatch时,都要更新视图的;那么把setState方法进行订阅;
return <div>
<button onClick={this.add}>+</button>
{this.props.num}
<button onClick={this.min}>-</button>
</div>
}
}
// connect : 第一个参数: 函数 第二个参数: 是当前组件
// actions : 是一个返回类型的对象;
// mapStateToProps\mapDisPatchToProps都是在connect函数内部调用的
let mapStateToProps=state=>({...state.counter}); // 当执行connect时,会默认调用这个箭头函数,
并且将store中的state数据传给当前的函数state参数;返回当前组件对应的数据,并且放在了当前组件的行间属性上;
let mapDisPatchToProps=(dispatch)=>{// dispatch==>store.dispatch
return {// 这个对象会放在组件的属性上;
add:()=>{dispatch(actions.add())},
min:()=>{dispatch(actions.min())}
}
}
// 都是将这两个函数的返回值放到组件的属性上;
export default connect(mapStateToProps,actions)(Counter);
//在执行connect时,判断第二个参数是否是一个函数,如果是函数,则将函数的执行结果放在组件的行间属性上,
如果是一个对象,那么会默认调用一个bindActionsCreator这个方法,将该方法的返回值放在组件行间属性上
当前属性传给组件
// action 就是connect传入的action dispatch是store中的dispatch方法
// let bindActionCreator=(action,dispatch)=>{
// let obj ={};
// for(let key in action){
// obj[key]= ()=>{
// dispatch(action[key]())
// }
// }
// return obj;
// }
page/2.compute
import React from "react";
// import store from "../store";
import {connect} from "react-redux";
// import actions from "../store/actions/counter";
class Compute extends React.Component{
// constructor(){
// super();
// this.state={num:store.getState().counter.num}
// }
// componentDidMount(){
// // react中更新视图: setState props
// store.subscribe(()=>{
// this.setState({num:store.getState().counter.num})
// })
// }
render(){
return <div>
{this.props.num%2===0?"偶数":"奇数"}
</div>
}
}
export default connect(state=>({...state.counter}))(Compute)
store/actions/counter.js
import * as types from "../action-type";
export default {
add(){
// 这个地方要发请求的;
return {type:types.ADD_COUNT}
},
min(){
return {type:types.MIN_COUNT}
}
}
store/reducers/counter.js
import * as types from "../action-type";
let initState ={num:0};
// 因为counter和compute共用一个数据;所以不能放在counter自己的私有属性上,
而且它们没有父子关系,没办法直接通过props进行传递,所以将数据放在公共的store上,实现数据共享;
function counter(state=initState,action){
switch(action.type){
case types.ADD_COUNT:
return {...state,num:state.num+1};
case types.MIN_COUNT:
return {...state,num:state.num-1}
}
return state;// 初始化默认值;
}
export default counter;
store/reducers/index.js
import {combineReducers} from "redux";
import counter from "./counter.js";
let reducer = combineReducers({
counter:counter
});
export default reducer;
store/index.js
import {createStore} from "redux";
import reducer from "./reducers/index.js";
let store = createStore(reducer);
export default store;
store/action-type.js
export const ADD_COUNT="ADD_COUNT";
// 导出常量
export const MIN_COUNT="MIN_COUNT";
index.js
import React from "react";
import ReactDOM from "react-dom";
import Counter from "./page/1.counter";
import Compute from "./page/2.compute.js";
// 将store注入到每一个组件;需要从react-redux解构出Provider这个组件,
然后将所有的组件嵌套在Provider组件里面;
import {Provider} from "react-redux";
import store from "./store/index";
ReactDOM.render(
<Provider store={store}>
<Counter></Counter>
<Compute></Compute>
</Provider>
,document.getElementById("root"));