弹窗类组件设计与实现
设计思路
弹窗类组件的要求弹窗内容在A处声明,却在B处展示。react中相当于弹窗内容看起来被render到⼀个组件⾥⾯去,实际改变的是⽹⻚上另⼀处的DOM结构,这个显然不符合正常逻辑。但是通过使⽤框架提供的特定API创建组件实例并指定挂载⽬标仍可完成任务。
常⻅⽤法如下:Dialog在当前组件声明,但是却在body中另⼀个div中显示
import React, {Component} from "react";
import Dialog from "../conponents/Dialog";
export default class DialogPage extends Component {
constructor(props) {
super(props);
this.state = {
showDialog: false
};
}
render() {
const {showDialog} = this.state;
return (
<div>
<h3>DialogPage</h3>
<button onClick={()=>this.setState({showDialog: !showDialog})}>
toggle
</button>
{showDialog && <Dialog />}
</div>
);
}}
具体实现: Portal
传送⻔,react v16之后出现的portal可以实现内容传送功能。
范例:Dialog组件 (Dialog.js)
import React, { Component } from "react";
import { createPortal } from "react-dom";
export default class Dialog extends Component {
constructor(props) {
super(props);
const doc = window.document;
this.node = doc.createElement("div");
doc.body.appendChild(this.node);
}
//卸载时,外界的div被清除
componentWillUnmount() {
window.document.body.removeChild(this.node);
}
render() {
const { hideDialog } = this.props;
return createPortal(
<div className="dialog">
{this.props.children}
{typeof hideDialog === "function" && (
<button onClick={hideDialog}>关掉弹窗</button>
)}
</div>,
this.node,
);
}}
总结⼀下:Dialog做得事情是通过调⽤createPortal把要画的东⻄画在DOM树上另⼀个⻆落。
Reducer
reducer 就是⼀个纯函数,接收旧的 state 和 action,返回新的 state。
;(previousState, action) => newState
之所以将这样的函数称之为 reducer,是因为这种函数与被传Array.prototype.reduce(reducer, ?initialValue) ⾥的回调函数属于相同的类型。保持 reducer 纯净⾮常重要。永远不要在 reducer ⾥做这些操作:
- 修改传⼊参数;
- 执⾏有副作⽤的操作,如 API 请求和路由跳转;
- 调⽤⾮纯函数,如
Date.now()或Math.random()。
什么是reduce
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator
+ currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
Redux 上⼿
Redux是JavaScript应⽤的状态容器。它保证程序⾏为⼀致性且易于测试。
安装redux
yarn add redux
redux较难上⼿,是因为上来就有太多的概念需要学习,⽤⼀个累加器举例:
- 需要⼀个store来存储数据
- store⾥的reducer初始化state并定义state修改规则
- 通过dispatch⼀个action来提交对数据的修改
- action提交到reducer函数⾥,根据传⼊的action的type,返回新的state
举个栗子
1、创建store,src/store/index.js
import {createStore} from "redux";
export const counterReducer = (state = 0, {type, payload = 1}) => {
switch (type) {
case "ADD":
return state + payload;
//如果state是对象
// return {...state, ...newState};
case "MINUS":
return state - payload;
default:
return state;
}
};
const store = createStore(countReducer);
export default store;
2、创建ReduxPage
import React, {Component} from "react";
import store from "../store/";
export default class ReduxPage extends Component {
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.forceUpdate();
});
}
componentWillUnmount() {
this.unsubscribe();
}
add = () => {
store.dispatch({type: "ADD"});
};
minus = () => {
store.dispatch({type: "MINUS"});
};
render() {
console.log("store", store); //sy-log
return (
<div>
<h3>ReduxPage</h3>
<p>{store.getState()}</p>
<button onClick={this.add}>add</button>
<button onClick{this.minus}>minus</button>
</div>
);
}}
注意:如果点击按钮不能更新,查看是否订阅(subscribe)状态变更。
3、还可以在src/index.js的render⾥订阅状态变更
import store from './store/'
const render = ()=>{
ReactDom.render( <App/>,
document.querySelector('#root')
)
}
render()
store.subscribe(render)