mirror of
https://github.com/evgen-app/chess-rpg-front.git
synced 2024-11-21 16:36:33 +03:00
first commit
This commit is contained in:
parent
013f71c0da
commit
5eb2b68299
94
package-lock.json
generated
94
package-lock.json
generated
|
@ -2013,6 +2013,30 @@
|
|||
"source-map": "^0.7.3"
|
||||
}
|
||||
},
|
||||
"@react-three/fiber": {
|
||||
"version": "8.0.22",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.0.22.tgz",
|
||||
"integrity": "sha512-HkQR+SKm0Bi4qq9sxCV2rv2aXxEhWPcj52bLOCiW3WpF8sGJtmdc/pP4ih5gXwN7I5NMTa2eDeoSYkIPOwt1/g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.17.8",
|
||||
"@types/react-reconciler": "^0.26.7",
|
||||
"react-reconciler": "^0.27.0",
|
||||
"react-use-measure": "^2.1.1",
|
||||
"scheduler": "^0.21.0",
|
||||
"suspend-react": "^0.0.8",
|
||||
"zustand": "^3.7.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"scheduler": {
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
|
||||
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@reduxjs/toolkit": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.2.tgz",
|
||||
|
@ -2622,6 +2646,14 @@
|
|||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-reconciler": {
|
||||
"version": "0.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
|
||||
"integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/resolve": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||
|
@ -2678,6 +2710,15 @@
|
|||
"@types/jest": "*"
|
||||
}
|
||||
},
|
||||
"@types/three": {
|
||||
"version": "0.141.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.141.0.tgz",
|
||||
"integrity": "sha512-OJdKDgTPVBUgc+s74DYoy4aLznbFFC38Xm4ElmU1YwGNgR7GGFVvFCX7lpVgOsT6S1zSJtGdajTsOYE8/xY9nA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/webxr": "*"
|
||||
}
|
||||
},
|
||||
"@types/trusted-types": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz",
|
||||
|
@ -2688,6 +2729,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
|
||||
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
|
||||
},
|
||||
"@types/webxr": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.4.0.tgz",
|
||||
"integrity": "sha512-LQvrACV3Pj17GpkwHwXuTd733gfY+D7b9mKdrTmLdO7vo7P/o6209Qqtk63y/FCv/lspdmi0pWz6Qe/ull9kQg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
|
@ -4287,6 +4334,11 @@
|
|||
"whatwg-url": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
|
@ -9703,6 +9755,25 @@
|
|||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"react-reconciler": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz",
|
||||
"integrity": "sha512-HmMDKciQjYmBRGuuhIaKA1ba/7a+UsM5FzOZsMO2JYHt9Jh8reCb7j1eDC95NOyUlKM9KRyvdx0flBuDvYSBoA==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.21.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"scheduler": {
|
||||
"version": "0.21.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz",
|
||||
"integrity": "sha512-1r87x5fz9MXqswA2ERLo0EbOAU74DpIUO090gIasYTqlVoJeMcl+Z1Rg7WHz+qtPujhS/hGIt9kxZOYBV3faRQ==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-redux": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz",
|
||||
|
@ -9783,6 +9854,14 @@
|
|||
"workbox-webpack-plugin": "^6.4.1"
|
||||
}
|
||||
},
|
||||
"react-use-measure": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
|
||||
"integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
|
||||
"requires": {
|
||||
"debounce": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
@ -10714,6 +10793,11 @@
|
|||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
|
||||
},
|
||||
"suspend-react": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/suspend-react/-/suspend-react-0.0.8.tgz",
|
||||
"integrity": "sha512-ZC3r8Hu1y0dIThzsGw0RLZplnX9yXwfItcvaIzJc2VQVi8TGyGDlu92syMB5ulybfvGLHAI5Ghzlk23UBPF8xg=="
|
||||
},
|
||||
"svg-parser": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
|
||||
|
@ -10862,6 +10946,11 @@
|
|||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
|
||||
},
|
||||
"three": {
|
||||
"version": "0.141.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.141.0.tgz",
|
||||
"integrity": "sha512-JaSDAPWuk4RTzG5BYRQm8YZbERUxTfTDVouWgHMisS2to4E5fotMS9F2zPFNOIJyEFTTQDDKPpsgZVThKU3pXA=="
|
||||
},
|
||||
"throat": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
||||
|
@ -11816,6 +11905,11 @@
|
|||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
|
||||
},
|
||||
"zustand": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
|
||||
"integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@react-three/fiber": "^8.0.22",
|
||||
"@reduxjs/toolkit": "^1.8.2",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
|
@ -15,6 +16,7 @@
|
|||
"react-dom": "^18.1.0",
|
||||
"react-redux": "^8.0.2",
|
||||
"react-scripts": "5.0.1",
|
||||
"three": "^0.141.0",
|
||||
"typescript": "^4.7.3",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
|
@ -41,5 +43,8 @@
|
|||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.141.0"
|
||||
}
|
||||
}
|
||||
|
|
39
src/App.css
39
src/App.css
|
@ -1,39 +0,0 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-float infinite 3s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: rgb(112, 76, 182);
|
||||
}
|
||||
|
||||
@keyframes App-logo-float {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './app/store';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
const { getByText } = render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
|
||||
expect(getByText(/learn/i)).toBeInTheDocument();
|
||||
});
|
50
src/App.tsx
50
src/App.tsx
|
@ -1,56 +1,10 @@
|
|||
import React from 'react';
|
||||
import logo from './logo.svg';
|
||||
import { Counter } from './features/counter/Counter';
|
||||
import './App.css';
|
||||
import { Board } from './components/board';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<Counter />
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to reload.
|
||||
</p>
|
||||
<span>
|
||||
<span>Learn </span>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
React
|
||||
</a>
|
||||
<span>, </span>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://redux.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Redux
|
||||
</a>
|
||||
<span>, </span>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://redux-toolkit.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Redux Toolkit
|
||||
</a>
|
||||
,<span> and </span>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://react-redux.js.org/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
React Redux
|
||||
</a>
|
||||
</span>
|
||||
</header>
|
||||
<Board></Board>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
|
||||
import counterReducer from '../features/counter/counterSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
counter: counterReducer,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
44
src/components/board.tsx
Normal file
44
src/components/board.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React, { useEffect } from "react";
|
||||
import * as THREE from 'three';
|
||||
|
||||
import { Canvas, RootState, useFrame, useThree } from '@react-three/fiber'
|
||||
import { Cell } from "./cell";
|
||||
|
||||
function ThreeSettings(){
|
||||
const set = useThree((state)=>state.set)
|
||||
const cam = useThree((state)=>state.camera)
|
||||
useEffect(
|
||||
()=>{
|
||||
cam.far = 500
|
||||
cam.near = 0.1
|
||||
cam.updateProjectionMatrix();
|
||||
|
||||
cam.rotation.set(-Math.PI/4,0,0)
|
||||
cam.position.set(0,6,7)
|
||||
set({
|
||||
size:{width:512, height:512},
|
||||
})
|
||||
}
|
||||
)
|
||||
return(null);
|
||||
}
|
||||
export const Board:React.FC = () =>{
|
||||
let cells = new Array<JSX.Element>()
|
||||
let color = true
|
||||
for (let i = 0; i<64; i++){
|
||||
cells.push(
|
||||
<Cell color={color? "white":"black"} position={[i%9 - 4,1,Math.floor(i/9)]}></Cell>
|
||||
)
|
||||
color = !color
|
||||
}
|
||||
return(
|
||||
<Canvas>
|
||||
<ThreeSettings/>
|
||||
<ambientLight />
|
||||
<pointLight position={[10, 10, 10]} />
|
||||
{
|
||||
cells
|
||||
}
|
||||
</Canvas>
|
||||
);
|
||||
}
|
22
src/components/cell.tsx
Normal file
22
src/components/cell.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React, { useRef, useState } from "react";
|
||||
import { Canvas, useFrame, Vector3 } from '@react-three/fiber'
|
||||
|
||||
interface CellIE{
|
||||
position:Vector3
|
||||
color: string
|
||||
}
|
||||
|
||||
|
||||
export const Cell:React.FC<CellIE> = (props:CellIE) => {
|
||||
const mesh = useRef()
|
||||
|
||||
return(
|
||||
<mesh
|
||||
position={props.position}
|
||||
ref={mesh as any}
|
||||
scale={1}>
|
||||
<boxGeometry args={[1, 0.5, 1]} />
|
||||
<meshStandardMaterial color={props.color} />
|
||||
</mesh>
|
||||
);
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.row > button {
|
||||
margin-left: 4px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.row:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 78px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
margin-top: 2px;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
||||
|
||||
.button {
|
||||
appearance: none;
|
||||
background: none;
|
||||
font-size: 32px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
outline: none;
|
||||
border: 2px solid transparent;
|
||||
color: rgb(112, 76, 182);
|
||||
padding-bottom: 4px;
|
||||
cursor: pointer;
|
||||
background-color: rgba(112, 76, 182, 0.1);
|
||||
border-radius: 2px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.textbox {
|
||||
font-size: 32px;
|
||||
padding: 2px;
|
||||
width: 64px;
|
||||
text-align: center;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus {
|
||||
border: 2px solid rgba(112, 76, 182, 0.4);
|
||||
}
|
||||
|
||||
.button:active {
|
||||
background-color: rgba(112, 76, 182, 0.2);
|
||||
}
|
||||
|
||||
.asyncButton {
|
||||
composes: button;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.asyncButton:after {
|
||||
content: '';
|
||||
background-color: rgba(112, 76, 182, 0.15);
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
transition: width 1s linear, opacity 0.5s ease 1s;
|
||||
}
|
||||
|
||||
.asyncButton:active:after {
|
||||
width: 0%;
|
||||
opacity: 1;
|
||||
transition: 0s;
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
||||
import {
|
||||
decrement,
|
||||
increment,
|
||||
incrementByAmount,
|
||||
incrementAsync,
|
||||
incrementIfOdd,
|
||||
selectCount,
|
||||
} from './counterSlice';
|
||||
import styles from './Counter.module.css';
|
||||
|
||||
export function Counter() {
|
||||
const count = useAppSelector(selectCount);
|
||||
const dispatch = useAppDispatch();
|
||||
const [incrementAmount, setIncrementAmount] = useState('2');
|
||||
|
||||
const incrementValue = Number(incrementAmount) || 0;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.row}>
|
||||
<button
|
||||
className={styles.button}
|
||||
aria-label="Decrement value"
|
||||
onClick={() => dispatch(decrement())}
|
||||
>
|
||||
-
|
||||
</button>
|
||||
<span className={styles.value}>{count}</span>
|
||||
<button
|
||||
className={styles.button}
|
||||
aria-label="Increment value"
|
||||
onClick={() => dispatch(increment())}
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<input
|
||||
className={styles.textbox}
|
||||
aria-label="Set increment amount"
|
||||
value={incrementAmount}
|
||||
onChange={(e) => setIncrementAmount(e.target.value)}
|
||||
/>
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={() => dispatch(incrementByAmount(incrementValue))}
|
||||
>
|
||||
Add Amount
|
||||
</button>
|
||||
<button
|
||||
className={styles.asyncButton}
|
||||
onClick={() => dispatch(incrementAsync(incrementValue))}
|
||||
>
|
||||
Add Async
|
||||
</button>
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={() => dispatch(incrementIfOdd(incrementValue))}
|
||||
>
|
||||
Add If Odd
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// A mock function to mimic making an async request for data
|
||||
export function fetchCount(amount = 1) {
|
||||
return new Promise<{ data: number }>((resolve) =>
|
||||
setTimeout(() => resolve({ data: amount }), 500)
|
||||
);
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import counterReducer, {
|
||||
CounterState,
|
||||
increment,
|
||||
decrement,
|
||||
incrementByAmount,
|
||||
} from './counterSlice';
|
||||
|
||||
describe('counter reducer', () => {
|
||||
const initialState: CounterState = {
|
||||
value: 3,
|
||||
status: 'idle',
|
||||
};
|
||||
it('should handle initial state', () => {
|
||||
expect(counterReducer(undefined, { type: 'unknown' })).toEqual({
|
||||
value: 0,
|
||||
status: 'idle',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle increment', () => {
|
||||
const actual = counterReducer(initialState, increment());
|
||||
expect(actual.value).toEqual(4);
|
||||
});
|
||||
|
||||
it('should handle decrement', () => {
|
||||
const actual = counterReducer(initialState, decrement());
|
||||
expect(actual.value).toEqual(2);
|
||||
});
|
||||
|
||||
it('should handle incrementByAmount', () => {
|
||||
const actual = counterReducer(initialState, incrementByAmount(2));
|
||||
expect(actual.value).toEqual(5);
|
||||
});
|
||||
});
|
|
@ -1,84 +0,0 @@
|
|||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { RootState, AppThunk } from '../../app/store';
|
||||
import { fetchCount } from './counterAPI';
|
||||
|
||||
export interface CounterState {
|
||||
value: number;
|
||||
status: 'idle' | 'loading' | 'failed';
|
||||
}
|
||||
|
||||
const initialState: CounterState = {
|
||||
value: 0,
|
||||
status: 'idle',
|
||||
};
|
||||
|
||||
// The function below is called a thunk and allows us to perform async logic. It
|
||||
// can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
|
||||
// will call the thunk with the `dispatch` function as the first argument. Async
|
||||
// code can then be executed and other actions can be dispatched. Thunks are
|
||||
// typically used to make async requests.
|
||||
export const incrementAsync = createAsyncThunk(
|
||||
'counter/fetchCount',
|
||||
async (amount: number) => {
|
||||
const response = await fetchCount(amount);
|
||||
// The value we return becomes the `fulfilled` action payload
|
||||
return response.data;
|
||||
}
|
||||
);
|
||||
|
||||
export const counterSlice = createSlice({
|
||||
name: 'counter',
|
||||
initialState,
|
||||
// The `reducers` field lets us define reducers and generate associated actions
|
||||
reducers: {
|
||||
increment: (state) => {
|
||||
// Redux Toolkit allows us to write "mutating" logic in reducers. It
|
||||
// doesn't actually mutate the state because it uses the Immer library,
|
||||
// which detects changes to a "draft state" and produces a brand new
|
||||
// immutable state based off those changes
|
||||
state.value += 1;
|
||||
},
|
||||
decrement: (state) => {
|
||||
state.value -= 1;
|
||||
},
|
||||
// Use the PayloadAction type to declare the contents of `action.payload`
|
||||
incrementByAmount: (state, action: PayloadAction<number>) => {
|
||||
state.value += action.payload;
|
||||
},
|
||||
},
|
||||
// The `extraReducers` field lets the slice handle actions defined elsewhere,
|
||||
// including actions generated by createAsyncThunk or in other slices.
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(incrementAsync.pending, (state) => {
|
||||
state.status = 'loading';
|
||||
})
|
||||
.addCase(incrementAsync.fulfilled, (state, action) => {
|
||||
state.status = 'idle';
|
||||
state.value += action.payload;
|
||||
})
|
||||
.addCase(incrementAsync.rejected, (state) => {
|
||||
state.status = 'failed';
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
|
||||
|
||||
// The function below is called a selector and allows us to select a value from
|
||||
// the state. Selectors can also be defined inline where they're used instead of
|
||||
// in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
|
||||
export const selectCount = (state: RootState) => state.counter.value;
|
||||
|
||||
// We can also write thunks by hand, which may contain both sync and async logic.
|
||||
// Here's an example of conditionally dispatching actions based on current state.
|
||||
export const incrementIfOdd =
|
||||
(amount: number): AppThunk =>
|
||||
(dispatch, getState) => {
|
||||
const currentValue = selectCount(getState());
|
||||
if (currentValue % 2 === 1) {
|
||||
dispatch(incrementByAmount(amount));
|
||||
}
|
||||
};
|
||||
|
||||
export default counterSlice.reducer;
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g fill="#764ABC"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></svg>
|
Before Width: | Height: | Size: 1.1 KiB |
Loading…
Reference in New Issue
Block a user