作用
作用:将electron的ipc通信功能封装成组件形式,当接收得主进程的ipc消息会自动更新redux中store中的数据,当用户通过专门的方法修改store的时候,又会自动更新store并且把消息发送给主进程。这样的好处是一些展示类的信息就不需要频繁注册事件,只需要组件绑定store就可以。
使用示例:
//使用示例
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import type { Store } from '../reducers/types';
import Routes from '../Routes';
import { ConnectIPC } from '../components/ConnectIPC';
export default function Root(Props) {
const { store, history } = Props;
return (
<Provider store={store}>
//组件需要包裹在reduxProvider中,通过这种方式,将ipc和redux连接起来
<ConnectIPC>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</ConnectIPC>
</Provider>
);
}
实现
思路:思路大致和ConnectedRouter组件的实现差不多,在ConnectIPC中,通过订阅ipcRenderer的事件来获取主进程发过来的消息,通过订阅redux中store的subscribe来获取用户主动发送的数据。
遇到的问题:
1、如何获取store到ConnectIPC中?
React提供了一个提供者模式,这个模式的使用如下:
import { createContext } from 'react';
let { Provider, Consumer } = createContext();
//被IPCProvider包裹的子组件都可以通过IPCConsumer利用render props模式获取到值
export let IPCProvider = Provider;
export let IPCConsumer = Consumer;
Redux也提供了类似的方式
//通过这种方式获取到Redux的提供者上下文
import { ReactReduxContext } from 'react-redux';
将store注入到组件中去
function ConnectIPCWithContext(props) {
let Context = props.context || ReactReduxContext;
if (Context == null) {
throw 'Please upgrade to react-redux v6';
}
return <Context.Consumer>{({ store }) => <ConnectIPC store={store} {...props} />}</Context.Consumer>;
}
2、如何实现子组件被ConnectIPC包裹,并且实现侦听改变state 以下是ConnectIPC的实现
class ConnectIPC extends Component {
constructor(props) {
super(props);
//获取上面ConnectIPCWithContext组件注入进来的store onIPCRenderMessage是action方法通过React-Redux的connect组件注入进来的。
let { store, onIPCRenderMessage, children } = props;
//返回卸载方法,侦听
//注册IPC事件,wrapIpcRender方法里面注册事件,并且将取消事件的方法返回来,可以在组件生命周期卸载的时候,取消注册。
this.unipcRender = wrapIpcRender((event, arg) => {
//接受消息
onIPCRenderMessage(arg);
});
this.lastSendMsg = {};
//当用户主动向主进程发送数据的时候,也应该是通过操作store,为了防止数据重复发送,所以每一次的数据都会有uuid保证唯一。
this.unSubScribe = store.subscribe(() => {
//侦听数据变更
//判断数据变更
//对象的比较问题
console.log(`侦听`)
let { sendMsg } = store.getState();
//判断uuid
// if (this.lastSendMsg.uuid === sendMsg.uuid) {
// return;
// }
//发送信息到ipc
ipcRenderer.send('channel', sendMsg);
// this.lastSendMsg = sendMsg;
});
//提供给用户的修改store的方法,sendIPCRenderMessage是个action,用来触发修改数据
this.wrapIpcRendererMsg = wrapIpcRendererMsg(store, sendIPCRenderMessage);
}
//卸载事件
componentWillUnmount() {
this.unipcRender();
this.unSubScribe();
}
render() {
let { children } = this.props;
return (
//使用IPC的提供者包裹子组件
<IPCProvider value={{ sendIpcRendererMsg: this.wrapIpcRendererMsg }}>
//保证子组件唯一
{React.Children.only(children)}
</IPCProvider>
);
}
}
}
//wrapIpcRender函数实现
import {ipcRenderer} from 'electron'
export function wrapIpcRender(cb){
ipcRenderer.on('channel',cb);
return function(){
ipcRenderer.removeListener('channel',cb);
}
export function wrapIpcRendererMsg(store,action){
// ipcRenderer.send(msg)
return (args)=>{
// console.log( `全部数据是${JSON.stringify(store.getState())}`)
store.dispatch(action(args));
}
}
3、如何对有需要的组件注入sendIpcRendererMsg方法?
使用高阶组件类似Redux的connect,补充知识点是ES6的类装饰器函数如果返回不为false的值那个调用这个类的时候使用的就是这个值。
以下是注入的实现和使用
//实现
export function connectIPCSender() {
return (Component) => (props) => (
//使用ipc获得提供者的数据
<IPCConsumer>
//将方法输入到子组件,但是高阶组件的规则是保证无关props的传递,所以需要再把props传递过去
{({ sendIpcRendererMsg }) => <Component sendIpcRendererMsg={sendIpcRendererMsg} {...props} />}
</IPCConsumer>
);
}
//使用对一个标题栏注入sendIpcRendererMsg方法
/注入ipc通信
@connectIPCSender()
export default class Heade extends Component{
render(){
console.log(this.props)
let {sendIpcRendererMsg}=this.props;
return (
<div onClick={()=>{console.log('dianji'); sendIpcRendererMsg(Date.now())}} className={styles.heade}>
</div>
)
}
}
概念性的东西
这其中主要用到的概念还是React文档中的那一套
1、高阶组件
2、Render Props
3、提供者模式
4、函数柯里化