開始
安裝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 [email protected] --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
就完成了。