From 67fa902c75e48602942164c61cc850fee7793e61 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Fri, 17 Jan 2025 09:22:59 +0300 Subject: [PATCH] createReducer & createAction --- package-lock.json | 20 ++++ package.json | 1 + src/__data__/actions/stars.ts | 41 ++++---- src/__data__/reducers/stars.ts | 52 +++++----- src/__data__/store.ts | 9 +- src/components/profile/from.tsx | 35 +++++-- src/components/profile/profile.tsx | 89 ++++++++++------ src/components/profileEdit/index.ts | 1 + src/components/profileEdit/profileEdit.tsx | 114 +++++++++++++++++++++ src/components/stars.tsx | 33 +++--- src/pages/friends.tsx | 15 ++- stubs/api/index.js | 1 - stubs/json/users/success.json | 2 + 13 files changed, 300 insertions(+), 113 deletions(-) create mode 100644 src/components/profileEdit/index.ts create mode 100644 src/components/profileEdit/profileEdit.tsx diff --git a/package-lock.json b/package-lock.json index 511ef69..cc1f315 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@brojs/cli": "1.6.1", "@chakra-ui/react": "^2.10.3", "@eslint/js": "^9.11.0", + "@redux-devtools/extension": "^3.3.0", "@stylistic/eslint-plugin": "^2.8.0", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", @@ -2437,6 +2438,19 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@redux-devtools/extension": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.3.0.tgz", + "integrity": "sha512-X34S/rC8S/M1BIrkYD1mJ5f8vlH0BDqxXrs96cvxSBo4FhMdbhU+GUGsmNYov1xjSyLMHgo8NYrUG8bNX7525g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2", + "immutable": "^4.3.4" + }, + "peerDependencies": { + "redux": "^3.1.0 || ^4.0.0 || ^5.0.0" + } + }, "node_modules/@remix-run/router": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", @@ -6348,6 +6362,12 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", diff --git a/package.json b/package.json index a9eba76..35f02ca 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@brojs/cli": "1.6.1", "@chakra-ui/react": "^2.10.3", "@eslint/js": "^9.11.0", + "@redux-devtools/extension": "^3.3.0", "@stylistic/eslint-plugin": "^2.8.0", "@types/react": "^18.3.12", "@types/react-dom": "^18.3.1", diff --git a/src/__data__/actions/stars.ts b/src/__data__/actions/stars.ts index 8061684..698cb13 100644 --- a/src/__data__/actions/stars.ts +++ b/src/__data__/actions/stars.ts @@ -1,23 +1,26 @@ import * as types from '../const' -export const setStars = (userId: string, value: number) => ({ - type: types.SET_STARS, - payload: { - id: userId, - value, - }, -}) +const createAction = (type: string) => (payload?: Payload) => ({ type, payload }) -export const incrementStart = (userId: string) => ({ - type: types.INCREMENT_STARS, - payload: { - id: userId - } -}) +export const setStars = createAction<{ id: string, value: number }>(types.SET_STARS) +export const incrementStars = createAction<{ id: string }>(types.INCREMENT_STARS) +export const decrementStars = createAction<{ id: string }>(types.DECREMENT_STARS) -export const decrementStart = (userId: string) => ({ - type: types.INCREMENT_STARS, - payload: { - id: userId - } -}) +// export const setStarsOld = (payload) => ({ +// type: types.SET_STARS, +// payload, +// }) + +// export const incrementStart = (userId: string) => ({ +// type: types.INCREMENT_STARS, +// payload: { +// id: userId +// } +// }) + +// export const decrementStart = (userId: string) => ({ +// type: types.INCREMENT_STARS, +// payload: { +// id: userId +// } +// }) diff --git a/src/__data__/reducers/stars.ts b/src/__data__/reducers/stars.ts index a5c2b29..08dad0b 100644 --- a/src/__data__/reducers/stars.ts +++ b/src/__data__/reducers/stars.ts @@ -5,34 +5,30 @@ const initialState = { user: {}, } -export const starsReducer = (state = initialState, action) => { - switch (action.type) { - case types.SET_STARS: - return { - ...state, - friends: { - ...state.friends, - [action.payload.id]: action.payload.value, - }, - } - case types.INCREMENT_STARS: - return { - ...state, - friends: { - ...state.friends, - [action.payload.id]: state[action.payload.id] + 1, - }, - } +const setStars = (state, action) => ({ + ...state, + friends: { + ...state.friends, + [action.payload.id]: action.payload.value, + }, +}) - case types.DECREMENT_STARS: - return { - ...state, - friends: { - ...state.friends, - [action.payload.id]: state[action.payload.id] - 1, - }, - } +const changeStars = (value) => (state, action) => ({ + ...state, + friends: { + ...state.friends, + [action.payload.id]: state.friends[action.payload.id] + value, + }, +}) - default: return state - } +const createReducer = (initialState, reducers) => (state = initialState, action) => { + const reducer = reducers[action.type] + if (!reducer) return state + return reducer(state, action) } + +export const starsReducer = createReducer(initialState, { + [types.SET_STARS]: setStars, + [types.INCREMENT_STARS]: changeStars(1), + [types.DECREMENT_STARS]: changeStars(-1), +}) diff --git a/src/__data__/store.ts b/src/__data__/store.ts index c6634b1..166a5dd 100644 --- a/src/__data__/store.ts +++ b/src/__data__/store.ts @@ -1,4 +1,5 @@ import { combineReducers, legacy_createStore } from 'redux' +import { devToolsEnhancer } from '@redux-devtools/extension' import { starsReducer } from './reducers/stars' @@ -6,7 +7,9 @@ export const store = legacy_createStore( combineReducers({ stars: starsReducer, }), -); - -(window as unknown as { redux: string }).redux = store as unknown as string + devToolsEnhancer({ + name: 'nav2', + }), +) +;(window as unknown as { redux: string }).redux = store as unknown as string diff --git a/src/components/profile/from.tsx b/src/components/profile/from.tsx index 9737ac8..2265799 100644 --- a/src/components/profile/from.tsx +++ b/src/components/profile/from.tsx @@ -1,13 +1,14 @@ import { useForm, Controller } from 'react-hook-form' -import { Box, Button, Input } from '@chakra-ui/react' +import { Box, Button, Input, Stack } from '@chakra-ui/react' import React, { useEffect, useRef } from 'react' +import { useDispatch } from 'react-redux' type Inputs = { name: string age: string } -export const FormTest = ({ name }) => { +export const FormTest = ({ name, age, onOpenModal, onCancel }) => { const { register, control, @@ -20,12 +21,20 @@ export const FormTest = ({ name }) => { mode: 'onChange', defaultValues: { name: name, - age: '12', + age: age, }, }) const onSibmit = ({ name, age }) => { - // console.log(1111111, name, age) + // console.log(name, age) + } + + const dispatch = useDispatch() + + const handleOpenModalClick = () => { + const data = watch() + dispatch({ type: 'OPEN_MODAL', payload: data }) + onOpenModal() } return ( @@ -34,7 +43,7 @@ export const FormTest = ({ name }) => { as="form" flexDirection="column" display="flex" - onSubmit={handleSubmit(onSibmit)} + onSubmit={handleSubmit(console.log)} > @@ -54,12 +63,22 @@ export const FormTest = ({ name }) => { ( + render={({ field }) => ( )} /> - + + + + + ) diff --git a/src/components/profile/profile.tsx b/src/components/profile/profile.tsx index 4e5ba0c..dd90322 100644 --- a/src/components/profile/profile.tsx +++ b/src/components/profile/profile.tsx @@ -20,10 +20,13 @@ import { FormTest } from './from' import { Stars } from '../stars' import { stars as starsContext } from '../../__data__/context' import { useUsers } from '../../hooks' +import { useDispatch, useSelector } from 'react-redux' +import { decrementStars, incrementStars } from '../../__data__/actions/stars' type User = Record & { id: string name: string + age: number avatar?: string rated: number friends?: User[] @@ -35,18 +38,29 @@ export const Profile = ({ user, isLink, title, + onOpenModal, }: { user: User isLink?: boolean title?: string + onOpenModal?: () => void }) => { // const [rated, setRated] = useState(user.rated || 0) const [editProfile, setEditProfile] = useState(false) return ( - {!isLink && editProfile && } - {!editProfile && } + {!isLink && editProfile && ( + setEditProfile(false)} + onOpenModal={onOpenModal} + age={user.age} + name={user.name} + /> + )} + {onOpenModal && !editProfile && ( + + )} {title || 'Данные профиля'} @@ -73,12 +87,12 @@ export const Profile = ({ {/* {!isLink && features['buttons'] && user.friends?.map((friend) => ( */} - - {/* ))} */} + + {/* ))} */} ) } @@ -127,43 +141,50 @@ const Form = memo<{ initialState: string; onSubmit(value: string): void }>( Form.displayName = 'FormMemo' -const withStars = - (Component) => - ({ userId }) => { - const { stars, setStar } = useContext(starsContext) +// const withStars = +// (Component) => +// ( { userId }) => { +// const { stars, setStar } = useContext(starsContext) - const addStar = useCallback( - () => setStar(userId, stars[userId] + 1), - [userId, setStar], - ) - const subStar = useCallback( - () => setStar(userId, stars[userId] - 1), - [userId, setStar], - ) +// const addStar = useCallback( +// () => setStar(userId, stars[userId] + 1), +// [userId, setStar], +// ) +// const subStar = useCallback( +// () => setStar(userId, stars[userId] - 1), +// [userId, setStar], +// ) - return ( - - ) - } +// return ( +// +// ) +// } -const Counter = ({ stars, addStar, subStar, userId }: { userId: 'some-user-id' | '2'}) => { - const { rate, setUserRate } = useUsers((state) => state[userId].rated) +const Counter = ({ + // stars, addStar, subStar, + userId, +}: { + userId: string +}) => { + // const { rate, setUserRate } = useUsers((state) => state[userId].rated) + const rate = useSelector((store) => store.stars.friends[userId]) ?? 0 + const dispatch = useDispatch() return (
{rate} - - + +
) } -const CounterWithStars = withStars(Counter) +// const CounterWithStars = withStars(Counter) diff --git a/src/components/profileEdit/index.ts b/src/components/profileEdit/index.ts new file mode 100644 index 0000000..90b80fd --- /dev/null +++ b/src/components/profileEdit/index.ts @@ -0,0 +1 @@ +export { ProfileEditModal } from './profileEdit' diff --git a/src/components/profileEdit/profileEdit.tsx b/src/components/profileEdit/profileEdit.tsx new file mode 100644 index 0000000..dc1f891 --- /dev/null +++ b/src/components/profileEdit/profileEdit.tsx @@ -0,0 +1,114 @@ +import { Box, Button, Input, Stack } from '@chakra-ui/react' +import React from 'react' +import { Controller, useForm } from 'react-hook-form' +import { + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, +} from '@chakra-ui/react' + +type Inputs = { + name: string + lastName: string + + age: string +} + +export const ProfileEditModal = ({ onClose }) => { + const { + register, + control, + handleSubmit, + watch, + reset, + setValue, + formState: { errors }, + } = useForm({ + mode: 'onChange', + defaultValues: { + name: 'name', + lastName: 'string', + age: '12', + }, + }) + + const onSibmit = ({ name, age }) => { + // console.log(1111111, name, age) + } + + return ( + + + + Modal Title + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/components/stars.tsx b/src/components/stars.tsx index 9f713b3..3f74eac 100644 --- a/src/components/stars.tsx +++ b/src/components/stars.tsx @@ -2,10 +2,11 @@ import { HStack, Icon } from '@chakra-ui/react' import { FaRegStar, FaStar } from 'react-icons/fa6' import React, { useMemo, useState } from 'react' import { getConfigValue } from '@brojs/cli' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { stars } from '../__data__/context' import { useUsers } from '../hooks' +import { setStars } from '../__data__/actions/stars' const useStars = (currentRated, starsCount) => { @@ -29,7 +30,7 @@ export const Stars = ({ userId, }: { count: number - userId: 'some-user-id' | '2' + userId: string // rated: number // setRated: (rate: number) => void }) => { @@ -37,21 +38,23 @@ export const Stars = ({ // stars, // setRated: starsSetRated // } = useStars(rated, count) - const { setUserRate } = useUsers(state => state[userId].rated) + // const { setUserRate } = useUsers(state => state[userId].rated) const rate = useSelector((store) => store.stars.friends[userId]) ?? 0 + const dispatch = useDispatch() - const handleStarsClick = (stars: number) => { - setUserRate(userId, stars) - fetch(getConfigValue('dry-wash.api.url') + '/user-rate', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - userId, - stars, - }), - }) + const handleStarsClick = (value: number) => { + dispatch(setStars({ id: userId, value })) + // setUserRate(userId, stars) + // fetch(getConfigValue('dry-wash.api.url') + '/user-rate', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // userId, + // stars, + // }), + // }) } return ( diff --git a/src/pages/friends.tsx b/src/pages/friends.tsx index 14fea81..f7bd893 100644 --- a/src/pages/friends.tsx +++ b/src/pages/friends.tsx @@ -5,12 +5,14 @@ import { useDispatch } from 'react-redux' import { Profile } from '../components' import { users } from '../__data__/users' import { setStars } from '../__data__/actions/stars' +import { ProfileEditModal } from '../components/profileEdit' export const Friends = memo(() => { const [isLoading, setIsLoading] = useState(false) const [data, setData] = useState(null) const [error, setError] = useState(null) const dispatch = useDispatch() + const [showModal, setShowModal] = useState(false) useEffect(() => { const getUser = async () => { @@ -24,7 +26,7 @@ export const Friends = memo(() => { setData(data) Object.keys(data).forEach(userId => { - dispatch(setStars(userId, data[userId].rated)) + dispatch(setStars({ id: userId, value: data[userId].rated })) }) } else { throw 'Что-то пошло не так' @@ -43,10 +45,13 @@ export const Friends = memo(() => { if(!data || isLoading) return

loading...

return ( - - - - + <> + {showModal && setShowModal(false)} />} + + setShowModal(true)} user={data['some-user-id']} /> + setShowModal(true)} user={data['2']} /> + + ) }) diff --git a/stubs/api/index.js b/stubs/api/index.js index c61a156..f4ce897 100644 --- a/stubs/api/index.js +++ b/stubs/api/index.js @@ -17,7 +17,6 @@ timer.fast = timer(300) // router.use(timer.fast) router.post('/user-rate', (req, res) => { - console.log(req.body) res.status(500).send({ ok: false }) }) diff --git a/stubs/json/users/success.json b/stubs/json/users/success.json index 760d8f9..e6898ba 100644 --- a/stubs/json/users/success.json +++ b/stubs/json/users/success.json @@ -4,6 +4,7 @@ "some-user-id": { "id": "some-user-id", "name": "alexandr", + "age": 38, "surname": null, "email": null, "rated": 4, @@ -24,6 +25,7 @@ "name": "not alexandr", "surname": null, "email": null, + "age": 24, "rated": 5, "avatar": "https://www.gravatar.com/avatar/6e?d=robohash", "friends": [