Создание React + TypeScript приложения
Первым делом нужно установить нужные библиотеки для нашего приложения
npm init electron-app@latest <имя_проекта> -- --template=webpack-typescript
npm install --save react react-dom
npm install --save-dev @types/react @types/react-dom
Тут подключается бутстрап и иконочные стили
npm install --save bootstrap
npm i font-awesome --save
Если мы хотим реализовать такую структуру папок
То нам нужно поменять пути в основном конфиге
forge.config.ts
const config: ForgeConfig = {
packagerConfig: {},
rebuildConfig: {},
makers: [
new MakerSquirrel({}),
new MakerZIP({}, ["darwin"]),
new MakerRpm({}),
new MakerDeb({}),
],
plugins: [
new WebpackPlugin({
mainConfig,
renderer: {
config: rendererConfig,
entryPoints: [
{
html: "./src/renderer/index.html",
js: "./src/renderer/renderer.tsx",
name: "main_window",
preload: {
js: "./src/preload/preload.ts",
},
},
],
},
}),
],
};
И так же поменять тут входную точку в мэйн процесс
webpack.main.config.ts
export const mainConfig: Configuration = {
/**
* This is the main entry point for your application, it's the first file
* that runs in the main process.
*/
entry: "./src/main/index.ts",
// Put your normal webpack config below here
module: {
rules,
},
resolve: {
extensions: [".js", ".ts", ".jsx", ".tsx", ".css", ".json"],
},
};
Так выглядит основной документ
renderer > index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>Electron React Typescript App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div class="root" id="root"></div>
</body>
</html>
Это файл стилей, который подключен к основной странице
body {
margin: 50px 0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
"Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
Так выглядит основной файл React
renderer > renderer.tsx
import React, { StrictMode } from "react";
import ReactDOM from "react-dom/client";
// тут подключаются стили
import "./index.css";
// тут подключается сторонний компонент реакта
import App from "./components/app/app";
// подключаем нужные файлики из наших библиотек иконочных шрифтов и бутстрапа
import '../../node_modules/bootstrap/dist/css/bootstrap.min.css';
import '../../node_modules/font-awesome/css/font-awesome.css';
const root: ReactDOM.Root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<StrictMode>
<App />
</StrictMode>
);
Примерно такие пути в проекте renderer
Так выглядит первый наш подключенный компонент со всем приложением
renderer > app > app.tsx
import React, {Component} from "react";
import AppInfo from "../app-info/app-info";
import SearchPanel from "../search-panel/search-panel";
import AppFilter from "../app-filter/app-filter";
import EmployeesList from "../employees-list/employees-list";
import EmployeesAddForm from "../employees-add-form/employees-add-form";
import "./app.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [
{name: 'Johnathan', salary: 800, increase: false, liked: true, id: 1},
{name: "Cloose", salary: 1800, increase: true, liked: false, id: 2},
{name: "Angela", salary: 300, increase: false, liked: false, id: 3},
],
maxId: 4,
term: '', // Строчка, по которой будет происходить поиск сотрудника
filter: 'all' // Сюда запишем выбранный фильтр
}
};
// Объединённый метод, который хотим передать вниз
onToggleProp = (id, prop) => {
this.setState(({data}) => ({
data: data.map(item => {
// Если id айтема равен id искомого объекта, то
if (item.id === id) {
// ... мы возвращаем новый объект со свойствами, которые было до и инвертированный increase
return {...item, [prop]: !item[prop]}
}
return item;
})
}))
}
deleteItem = (id) => {
this.setState(({data}) => {
return { data: data.filter(elem => elem.id !== id) };
});
}
addItem = (name, salary) => {
const newItem = {
name: name,
salary: salary,
increase: false,
liked: false,
id: this.maxId++
}
this.setState(({data}) => {
const newArr = [...data, newItem];
return {
data: newArr
}
});
}
searchEmployee = (items, term) => {
// Если ничего не введено в поиск, то показываем все объекты
if (term.length === 0) return items;
return items.filter(item => {
// indexOf() - поиск подстроки
// Возвращаем только те элементы, где присутствует term
return item.name.indexOf(term) > -1;
});
}
// Метод, который будет регистрировать изменения стейта данных
onUpdateSearch = (term) => {
// {term: term}
this.setState({term});
}
// Выбор фильтра
filterPost = (items, filter) => {
switch (filter) {
case 'onIncrease':
return items.filter(item => item.liked);
case 'moreSalary':
return items.filter(item => item.salary > 1000);
default:
return items;
}
}
onFilterSelect = (filter) => {
// {filter: filter}
this.setState({filter});
}
render() {
// Данные для реализации фильтрации на странице
const {term, data, filter} = this.state;
// Сразу передаём только те данные, которые подходят по поиску
const visibleData = this.filterPost(this.searchEmployee(data, term), filter);
// Считаем количество сотрудников
const employees = this.state.data.length;
// Количество сотрудников, идущих на повышение
const increased = this.state.data.filter(item => item.increase === true).length;
return (
<div className="app">
<AppInfo
employees={employees}
increased={increased}
/>
<div className="search-panel">
<SearchPanel onUpdateSearch={this.onUpdateSearch}/>
{/*Чтобы сделать анимацию кнопки, нужно передать фильтр*/}
<AppFilter
onFilterSelect={this.onFilterSelect}
filter={filter}
/>
</div>
<EmployeesList
// Это и есть пропсы
// Сюда передаём отфильтрованные данные
data={visibleData}
onDelete={this.deleteItem}
// А это те методы, которые передадутся в качестве props
onToggleProp={this.onToggleProp}
/>
<EmployeesAddForm
onAdd={this.addItem}
/>
</div>
);
}
}
export default App;
В конфиге ТСки нужно добавить строчку "jsx": "react-jsx"
, которая скажет, что мы работаем с библиотекой реакта
tsconfig.json
{
"compilerOptions": {
"target": "ES6",
"allowJs": true,
"jsx": "react-jsx",
"module": "commonjs",
"skipLibCheck": true,
"esModuleInterop": true,
"noImplicitAny": false,
"sourceMap": true,
"baseUrl": ".",
"outDir": "dist",
"moduleResolution": "node",
"resolveJsonModule": true,
"paths": {
"*": ["node_modules/*"]
}
},
"include": ["src/**/*"]
}
Итоговая структура проекта выглядит так: