thunk
This commit is contained in:
parent
67fa902c75
commit
4b869ffe7a
51
package-lock.json
generated
51
package-lock.json
generated
@ -13,6 +13,7 @@
|
||||
"@chakra-ui/react": "^2.10.3",
|
||||
"@eslint/js": "^9.11.0",
|
||||
"@redux-devtools/extension": "^3.3.0",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"@stylistic/eslint-plugin": "^2.8.0",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
@ -28,6 +29,7 @@
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"typescript-eslint": "^8.6.0"
|
||||
}
|
||||
},
|
||||
@ -2451,6 +2453,40 @@
|
||||
"redux": "^3.1.0 || ^4.0.0 || ^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.0.tgz",
|
||||
"integrity": "sha512-awNe2oTodsZ6LmRqmkFhtb/KH03hUhxOamEQy411m3Njj3BbFvoBovxo4Q1cBWnV1ErprVj9MlF0UPXkng0eyg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"immer": "^10.0.3",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"reselect": "^5.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
|
||||
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@reduxjs/toolkit/node_modules/immer": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
|
||||
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz",
|
||||
@ -8621,6 +8657,15 @@
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/redux-thunk": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
|
||||
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"redux": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz",
|
||||
@ -8737,6 +8782,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.8",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
|
||||
|
@ -20,6 +20,7 @@
|
||||
"@chakra-ui/react": "^2.10.3",
|
||||
"@eslint/js": "^9.11.0",
|
||||
"@redux-devtools/extension": "^3.3.0",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"@stylistic/eslint-plugin": "^2.8.0",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
@ -35,6 +36,7 @@
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^6.23.1",
|
||||
"redux": "^5.0.1",
|
||||
"redux-thunk": "^3.1.0",
|
||||
"typescript-eslint": "^8.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as types from '../const'
|
||||
import { createAction } from '@reduxjs/toolkit'
|
||||
|
||||
const createAction = <Payload>(type: string) => (payload?: Payload) => ({ type, payload })
|
||||
import * as types from '../const'
|
||||
|
||||
export const setStars = createAction<{ id: string, value: number }>(types.SET_STARS)
|
||||
export const incrementStars = createAction<{ id: string }>(types.INCREMENT_STARS)
|
||||
|
32
src/__data__/actions/users.ts
Normal file
32
src/__data__/actions/users.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import * as types from '../const'
|
||||
|
||||
import { setStars } from './stars'
|
||||
|
||||
const createAction = <Payload>(type: string) => (payload?: Payload) => ({ type, payload })
|
||||
|
||||
const fetchUsers = createAction(types.USER_FETCH)
|
||||
const success = createAction(types.USER_FETCH_SUCCESS)
|
||||
const error = createAction<string>(types.USER_FETCH_ERROR)
|
||||
export const reset = createAction(types.USER_FETCH_RESET)
|
||||
|
||||
export const getUsers = () => async (dispatch, getState) => {
|
||||
dispatch(fetchUsers())
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/users/')
|
||||
|
||||
if (response.ok) {
|
||||
const data = (await response.json()).body
|
||||
dispatch(success(data))
|
||||
|
||||
Object.keys(data).forEach(userId => {
|
||||
dispatch(setStars({ id: userId, value: data[userId].rated }))
|
||||
})
|
||||
} else {
|
||||
throw 'Что-то пошло не так'
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
dispatch(error('Что-то пошло не так'))
|
||||
}
|
||||
}
|
@ -1,3 +1,8 @@
|
||||
export const SET_STARS = 'SET_STARS'
|
||||
export const INCREMENT_STARS = 'INCREMENT_STARS'
|
||||
export const DECREMENT_STARS = 'DECREMENT_STARS'
|
||||
|
||||
export const USER_FETCH = 'USER_FETCH'
|
||||
export const USER_FETCH_SUCCESS = 'USER_FETCH_SUCCESS'
|
||||
export const USER_FETCH_ERROR = 'USER_FETCH_ERROR'
|
||||
export const USER_FETCH_RESET = 'USER_FETCH_RESET'
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
|
||||
import * as types from '../const'
|
||||
|
||||
const initialState = {
|
||||
@ -21,7 +23,7 @@ const changeStars = (value) => (state, action) => ({
|
||||
},
|
||||
})
|
||||
|
||||
const createReducer = (initialState, reducers) => (state = initialState, action) => {
|
||||
const createReducer = <State>(initialState: State, reducers) => (state = initialState, action): State => {
|
||||
const reducer = reducers[action.type]
|
||||
if (!reducer) return state
|
||||
return reducer(state, action)
|
||||
@ -32,3 +34,15 @@ export const starsReducer = createReducer(initialState, {
|
||||
[types.INCREMENT_STARS]: changeStars(1),
|
||||
[types.DECREMENT_STARS]: changeStars(-1),
|
||||
})
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'stars',
|
||||
initialState: {},
|
||||
reducers: {
|
||||
setStars(state, action) {
|
||||
state[action.payload.id] = action.payload.value
|
||||
},
|
||||
changeStars,
|
||||
},
|
||||
|
||||
})
|
||||
|
50
src/__data__/reducers/users.ts
Normal file
50
src/__data__/reducers/users.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import * as types from '../const'
|
||||
|
||||
const createReducer = <State>(initialState: State, reducers) => (state = initialState, action): State => {
|
||||
const reducer = reducers[action.type]
|
||||
if (!reducer) return state
|
||||
return reducer(state, action)
|
||||
}
|
||||
|
||||
export enum Statuses {
|
||||
IDLE = 'IDLE',
|
||||
FETCHING = 'FETCHING',
|
||||
SUCCESS = 'SUCCESS',
|
||||
ERROR = 'ERROR',
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
data: null,
|
||||
status: Statuses.IDLE,
|
||||
error: null as null | string,
|
||||
}
|
||||
|
||||
const startFetching = (state) => ({
|
||||
...state,
|
||||
error: null,
|
||||
status: Statuses.FETCHING,
|
||||
})
|
||||
|
||||
const fetchSuccess = (state, action) => ({
|
||||
...state,
|
||||
error: null,
|
||||
data: action.payload,
|
||||
status: Statuses.SUCCESS,
|
||||
})
|
||||
|
||||
const fetchError = (state, action) => ({
|
||||
...state,
|
||||
error: action.payload,
|
||||
status: Statuses.ERROR,
|
||||
})
|
||||
|
||||
const reset = () => ({
|
||||
...initialState
|
||||
})
|
||||
|
||||
export const reducer = createReducer(initialState, {
|
||||
[types.USER_FETCH]: startFetching,
|
||||
[types.USER_FETCH_SUCCESS]: fetchSuccess,
|
||||
[types.USER_FETCH_ERROR]: fetchError,
|
||||
[types.USER_FETCH_RESET]: reset,
|
||||
})
|
1
src/__data__/selectors/index.ts
Normal file
1
src/__data__/selectors/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * as userSelectors from './users'
|
8
src/__data__/selectors/rootSelector.ts
Normal file
8
src/__data__/selectors/rootSelector.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { createSelector } from '@reduxjs/toolkit'
|
||||
|
||||
import { StoreType } from '../store'
|
||||
|
||||
export const rootSelector = createSelector(
|
||||
(state: StoreType) => state,
|
||||
(state: StoreType) => state
|
||||
)
|
12
src/__data__/selectors/users.ts
Normal file
12
src/__data__/selectors/users.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { createSelector } from '@reduxjs/toolkit'
|
||||
|
||||
import { StoreType } from '../store'
|
||||
import { Statuses } from '../reducers/users'
|
||||
|
||||
import { rootSelector } from './rootSelector'
|
||||
|
||||
const usersRootSelector = createSelector(rootSelector, (state: StoreType) => state.user)
|
||||
|
||||
export const isLoading = createSelector(usersRootSelector, (state) => state.status === Statuses.FETCHING)
|
||||
export const data = createSelector(usersRootSelector, (state) => state.data)
|
||||
export const error = createSelector(usersRootSelector, (state) => state.error)
|
@ -1,15 +1,20 @@
|
||||
import { combineReducers, legacy_createStore } from 'redux'
|
||||
import { devToolsEnhancer } from '@redux-devtools/extension'
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
import { starsReducer } from './reducers/stars'
|
||||
import { reducer as usersFetchReducer } from './reducers/users'
|
||||
|
||||
export const store = legacy_createStore(
|
||||
combineReducers({
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
stars: starsReducer,
|
||||
}),
|
||||
devToolsEnhancer({
|
||||
name: 'nav2',
|
||||
}),
|
||||
)
|
||||
user: usersFetchReducer,
|
||||
},
|
||||
devTools: {
|
||||
name: 'stars',
|
||||
},
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware(),
|
||||
})
|
||||
|
||||
;(window as unknown as { redux: string }).redux = store as unknown as string
|
||||
export type StoreType = ReturnType<typeof store.getState>
|
||||
|
||||
export const useAppSelector = useSelector<StoreType>
|
||||
|
@ -14,10 +14,10 @@ export default () => <App/>
|
||||
|
||||
let rootElement: ReactDOM.Root
|
||||
|
||||
export const mount = async (Сomponent, element = document.getElementById('app')) => {
|
||||
export const mount = async (Сomponent, element = document.getElementById('app'), extraData) => {
|
||||
await config
|
||||
rootElement = ReactDOM.createRoot(element)
|
||||
rootElement.render(<Сomponent/>)
|
||||
rootElement.render(<Сomponent />)
|
||||
|
||||
if(module.hot) {
|
||||
module.hot.accept('./app', ()=> {
|
||||
|
@ -6,40 +6,25 @@ import { Profile } from '../components'
|
||||
import { users } from '../__data__/users'
|
||||
import { setStars } from '../__data__/actions/stars'
|
||||
import { ProfileEditModal } from '../components/profileEdit'
|
||||
import { getUsers } from '../__data__/actions/users'
|
||||
import { UnknownAction } from 'redux'
|
||||
import { useAppSelector } from '../__data__/store'
|
||||
import { Statuses } from '../__data__/reducers/users'
|
||||
import { userSelectors } from '../__data__/selectors'
|
||||
|
||||
export const Friends = memo(() => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [data, setData] = useState(null)
|
||||
const [error, setError] = useState(null)
|
||||
// const [isLoading, setIsLoading] = useState(false)
|
||||
// const [data, setData] = useState(null)
|
||||
// const [error, setError] = useState(null)
|
||||
const dispatch = useDispatch()
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
|
||||
const isLoading = useAppSelector(userSelectors.isLoading)
|
||||
const data = useAppSelector(userSelectors.data)
|
||||
const error = useAppSelector(userSelectors.error)
|
||||
|
||||
useEffect(() => {
|
||||
const getUser = async () => {
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/users/')
|
||||
|
||||
if (response.ok) {
|
||||
const data = (await response.json()).body
|
||||
setData(data)
|
||||
|
||||
Object.keys(data).forEach(userId => {
|
||||
dispatch(setStars({ id: userId, value: data[userId].rated }))
|
||||
})
|
||||
} else {
|
||||
throw 'Что-то пошло не так'
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
setError(e.message)
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
getUser()
|
||||
dispatch(getUsers() as unknown as UnknownAction)
|
||||
}, [])
|
||||
|
||||
if(!data || isLoading) return <h1>loading...</h1>
|
||||
|
Loading…
Reference in New Issue
Block a user