开始
安装react
脚手架并初始化项目
cnpm install -g create-react-app
create-react-app electron-react-today
cd electron-react-today
npm start
此时项目已经运行在 :localhost:3000
安装 electron
electron 7.0.0 实在太坑爹了
使用6.1.2
没有问题。
cnpm install --save-dev electron@6.1.2 --verbose
新建main.js
// 引入electron并创建一个Browserwindow
const { app, BrowserWindow } = require('electron')
const path = require('path')
const url = require('url')
// 保持window对象的全局引用,避免JavaScript对象被垃圾回收时,窗口被自动关闭.
let mainWindow
function createWindow () {
//创建浏览器窗口,宽高自定义具体大小你开心就好
mainWindow = new BrowserWindow({width: 800, height: 600})
/*
* 加载应用----- electron-quick-start中默认的加载入口
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))
*/
// 加载应用----适用于 react 项目
mainWindow.loadURL('http://localhost:3000/')
// 加载本地html
// mainWindow.loadFile('./index.html')
// 打开开发者工具,默认不打开
mainWindow.webContents.openDevTools()
// 关闭window时触发下列事件.
mainWindow.on('closed', function () {
mainWindow = null
})
}
// 当 Electron 完成初始化并准备创建浏览器窗口时调用此方法
app.on('ready', createWindow)
// 所有窗口关闭时退出应用.
app.on('window-all-closed', function () {
// macOS中除非用户按下 `Cmd + Q` 显式退出,否则应用与菜单栏始终处于活动状态.
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', function () {
// macOS中点击Dock图标时没有已打开的其余应用窗口时,则通常在应用中重建一个窗口
if (mainWindow === null) {
createWindow()
}
})
启动项目
在package.json
文件中添加:
{
"main": "main.js",
"scripts": {
"electron-start": "electron ."
},
}
然后执行:
npm run electron-start
看到如下页面,终于第一步完成了。
React Hooks
-
useState声明状态变量
const [ count , setCount ] = useState(0);
-
useEffect代替常用生命周期函数
// 第一次渲染和每次更新都会被执行 并且是异步执行的 useEffect(()=>{ console.log(`useEffect=>You clicked ${count} times`) }) // 当传空数组[]时,就是当组件将被销毁时才进行解绑, // 这也就实现了componentWillUnmount的生命周期函数 useEffect(()=>{ console.log('useEffect=>老弟你来了!Index页面') return ()=>{ console.log('老弟,你走了!Index页面') } },[])
-
useContext 实现数据共享(父子组件传值)
可以通过这个
hook
传递useReducer
产生的dispatch
函数。也就是不直接传递数据,而是传递修改数据的方法,在根组件中通过
reducer
修改状态。父组件
import React, { useState, createContext } from 'react'; import { Button } from '@material-ui/core'; // 引入子组件 import Num from './Num'; // 创建上下文对象 const CounterContext = createContext(); export default function Counter() { const [count, setCount] = useState(0); return ( <div> <p>点击了 {count} 次</p> <Button variant="contained" color="primary" onClick={() => setCount(count + 1)} > 点我加一 </Button> <CounterContext.Provider value={count}> <Num /> </CounterContext.Provider> </div> ); } // 导出上下文对象 export { CounterContext };
子组件
import React, { useContext } from 'react'; // 引入父组件的上下文 import { CounterContext } from './Counter'; export default function Count() { const count = useContext(CounterContext); return <h2>{count}</h2> }
-
useReducer 实现对复杂状态对象的管理
使用场景
对某个state有很多种操作
子组件需要修改上层组件的值,可以传递一个dispatch函数
const reducer = (state, action) => { switch(action.type) { case: "xx": .... } } const [state, dispatch] = useReducer(/*reducer函数*/ reducer, /*初始值*/ initialVal); dispatch({ type: 'xx', val: '' });
当然还有其他的Hooks,例如用于性能优化的useMemo
就不说了。
ToDoList 应用
有了以上内容就可以开发一个简单的TODoList
应用了。
使用到的内容:material-ui
和 上文中的React Hooks 。
至于如何安装组件库可以自行查看:https://material-ui.com/zh/ge...
整体目录结构如下图:
App.js 文件: 承载状态数据和组件。
import React, { useReducer, createContext } from 'react';
import { initialTodos, filterReducer, todosReducer } from './reducer/index';
import Filter from './components/Filter';
import AddTodo from './components/AddTodo';
import TodoList from './components/TodoList';
// 导出共享对象
export const AppContext = createContext();
function App() {
const [todos, dispatchTodos] = useReducer(todosReducer, initialTodos);
const [filterVal, dispatchFilter] = useReducer(filterReducer, 'ALL');
return (
<div style={{ margin: '20px 30px 0', maxWidth: 450 }}>
<AppContext.Provider value={dispatchTodos}>
<AddTodo />
<Filter dispatch={dispatchFilter} />
<TodoList filterVal={filterVal} todos={todos} />
</AppContext.Provider>
</div>
);
}
export default App;
/reducer/index.js 文件: 完成对数据的修改。
import uuid from 'uuid';
export const initialTodos = [
{
id: uuid(),
label: '学习React Hooks',
complete: false,
}, {
id: uuid(),
label: '吃饭睡觉',
complete: true,
}
];
export const filterReducer = (state, action) => {
switch (action.type) {
case 'SHOW_ALL':
return 'ALL';
case 'SHOW_COMPLETE':
return 'COMPLETE';
case 'SHOW_INCOMPLETE':
return 'INCOMPLETE';
default:
throw Error();
}
}
export const todosReducer = (state, action) => {
switch (action.type) {
case 'CHECK_TODO':
return state.map(todo => {
if (todo.id === action.id) {
todo.complete = !todo.complete
}
return todo;
});
case 'DELETE_TODO':
console.log(state, action.id)
return state.filter(todo => todo.id !== action.id);
case 'ADD_TODO':
return state.concat(action.todo);
default:
throw Error();
}
}
/components/AddTodo.js: 输入框,添加任务。
import React, { useState, useContext } from 'react';
import { Input, Button } from '@material-ui/core';
import uuid from 'uuid';
import { AppContext } from '../App';
export default function AddTodo() {
const dispatch = useContext(AppContext);
const handleSubmit = () => {
if (!task) return;
setTask('');
dispatch({ type: 'ADD_TODO', todo: { id: uuid(), label: task, complete: false } });
}
const [task, setTask] = useState('');
return (
<div style={{ display: 'flex' }}>
<Input
value={task}
style={{ flex: 1 }}
onChange={(e) => setTask(e.target.value)}
inputProps={{ 'aria-label': 'description' }}
/>
<Button color="primary" onClick={handleSubmit}>添加</Button>
</div>
)
}
/components/Filter.js: 筛选任务。
import React from 'react';
import { Button } from '@material-ui/core';
export default function Filter({ dispatch }) {
return (
<div style={{ display: 'flex', justifyContent: 'space-between', margin: '20px 0' }}>
<Button color="primary" onClick={() => dispatch({ type: 'SHOW_ALL' })}>全部</Button>
<Button color="primary" onClick={() => dispatch({ type: 'SHOW_COMPLETE' })}>已完成</Button>
<Button color="primary" onClick={() => dispatch({ type: 'SHOW_INCOMPLETE' })}>未完成</Button>
</div>
)
}
/components/TodoList.js: 展示所有的任务。
import React, { useContext } from 'react';
import { List, ListItem, ListItemText, ListItemSecondaryAction, IconButton, Checkbox, ListItemIcon } from '@material-ui/core';
import { Delete as DeleteIcon } from '@material-ui/icons';
import { AppContext } from '../App'
export default function TodoList({ todos, filterVal }) {
const dispatch = useContext(AppContext);
const deleteTodo = (item) => {
dispatch({ type: 'DELETE_TODO', id: item.id });
}
const checkTodo = (item) => {
dispatch({ type: 'CHECK_TODO', id: item.id });
}
// 过滤 todos
const filteredTodos = () => {
if (filterVal === 'ALL') return todos;
if (filterVal === 'COMPLETE') {
return todos.filter(todo => todo.complete);
}
if (filterVal === 'INCOMPLETE') {
return todos.filter(todo => !todo.complete);
}
return [];
}
return (
<List component="nav" aria-label="secondary mailbox folders">
{
filteredTodos().map(item => (
<ListItem key={item.id} button>
<ListItemIcon>
<Checkbox
edge="start"
checked={item.complete}
onChange={() => checkTodo(item)}
disableRipple
/>
</ListItemIcon>
<ListItemText primary={item.label} />
<ListItemSecondaryAction>
<IconButton onClick={() => deleteTodo(item)} edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
))
}
</List>
)
}
至此,一个简单的ToDoList
就完成了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。