? ????? ?? ??? ??? ????.
- CRUD ??
- ??? ??
- RTK ??? ??? Redux Persist
- ?? ?? URL ??
- ??? ?? ??
- ?? ?? ? ???
RTK ??? Redux Toolkit(RTK)? ??? ?? ??? ???? ? ?? ?????. ??? ????, ??, ????? ?? ???? ??? ?? Redux ???? ? ??? ???? API ?? ??? ??????. ?? ??? ??? ????:
- ?? ??: RTK ??? ???? ???? ???? ????? ???? ?? ????? UI? ?? ?? ???? ?????.
- ?? ???: ??? ???? RTK ??? ?? ?? ???? ?? ???? ?? ??? ??? ? ????. ??? ?? ???? ???? ?????? ??? ??? ?? ??? ??? ? ????.
- ?? ?? ??: RTK ??? ? API ?????? ?? ??? ????? ??? React ??(useGetPostsQuery, useCreatePostMutation ?)? ???? API? ??? ? ????.
- ?? ??: ????? ?? ??? ?? ??? ???? ?? ??? ?? ???? ??? ? ????.
- ???? Redux ??: RTK ??? Redux? ?? ????? ?? ?? ?? ?? ??? ?? ?? ?????? ???? ????.
RTK ??? React ??
React ??? RTK ??? ?? React ???????? ??? ???? ? ??? ?? ???? ????? ??? ?? ??? ????.
Feature | RTK Query | React Query |
---|---|---|
Purpose | Integrated within Redux for managing server data in Redux state. Best for apps already using Redux or requiring centralized global state. | Dedicated to managing server state with no Redux dependency. Great for apps focused on server state without Redux. |
Caching | Automatic caching with fine-grained cache invalidation through tags. Caches data globally within the Redux store. | Automatic caching with flexible cache control policies. Maintains a separate cache independent of Redux. |
Generated Hooks | Auto-generates hooks for endpoints, allowing mutations and queries using useQuery and useMutation hooks. | Provides hooks (useQuery, useMutation) that work independently from Redux, but require manual configuration of queries and mutations. |
DevTools | Integrated into Redux DevTools, making debugging seamless for Redux users. | Provides its own React Query DevTools, with detailed insight into query states and cache. |
Error Handling | Centralized error handling using Redux middleware. | Error handling within individual queries, with some centralized error-handling options. |
Redux Integration | Built directly into Redux, simplifying usage for Redux-based apps. | Not integrated with Redux by default, although Redux and React Query can be combined if needed. |
RTK ??? React ?? ??? ??:
-
RTK ?? ?? ??? ?? ??:
- ?? Redux? ???? ??? ??? ????? ?? ???? ???? ???? ????.
- Redux ??? ?? ??? ?? ?? ? ?? ?? ??? ?????.
-
React ?? ?? ??? ?? ??:
- Redux ???? ?? ?? ??? ??? ????.
- ??? ?? ?? ??? ???? ?? ? ??? ???? ????.
????? RTK ??? Redux ?? ??????? ??? ??, React ??? Redux? ?? ????? ?? ???? ?? ?? ??? ??? ?? ????? ???? ???? ?????.
1. ??? ?? ? ??
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
Redux Store(src/store/store.js): Redux ???? ??????? ??? ???? ?? ?????. ???? Redux ??? ?? ??? ??? ???? redux-persist? ???? ?? ?? ???? ?????.
-
redux-???:
- ??: Redux ??? ??? ? ?? ??? ?? ????? ???? ? ??? ???.
- ??: persistConfig ??? auth, postApi ? usersApi? ???? ???(?????? ??) ?????. ?, ?? ?? ??? ? ?? ???? ??????.
- persistReducer? ??? ??? ??? ??? ?????.
Enhancer: ??? ?? Enhancer? Redux ??, ?? ? ???? ??? ????? ? ??? ??? Reactotron? ?? ??? ???? ? ?????. ?? ?? ??? ?????? ????? ??? ?? ?? ???? ? ?????.
-
????:
- RTK ?? ????(postsApi.middleware, usersApi.middleware, authApi.middleware)? ?? ?? ?? ??? ???? ??? ????? ????? ????.
- rtkQueryErrorLogger: API ??? ??? ? ??? ?? ????? ??? ?????. RTK ??? isRejectedWithValue ??? ???? ??? ???? ????? ????? ??? ?? ???? ?? ??? ?? ? ????.
setupListeners: ? ??? ???? ?? ???? ?? ??? ??????? ?? ??? ?? ?? ?? ???? ??? ? ???? ???? ?? ??? ? ?? ???? ?? ?? ?? ?? ????? ??? ???? ??? ? ????.
2. RTK ??? ??? API ??
RTK ??? Redux ????, ?? ? ??? ?? ???? API ??? ??????. ??? ??? API? ?? ??? ??? ????.
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
-
authApi(src/api/authApi.js):
- ??? ?? ??? ?? ??(?: ??? ??, ????)? ??? ??? ??? ??? ?????.
- onQueryStarted: ???? ???? setToken ??? ???? ??? ??? Redux? ?????. ?? ?? ?? ?????? ?? ???? ??? ??? ??????.
// src/api/authApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { setToken } from '../features/auth/authSlice'; export const authApi = createApi({ reducerPath: 'authApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com/auth/', }), endpoints: (builder) => ({ login: builder.mutation({ query: (credentials) => ({ url: 'login', method: 'POST', body: credentials, }), async onQueryStarted(arg, { dispatch, queryFulfilled }) { try { const { data } = await queryFulfilled; dispatch(setToken(data.accessToken)); // Store the token in Redux } catch (error) { console.error('Login error:', error); } }, }), }), }); export const { useLoginMutation } = authApi;
-
postsApi(src/api/postsApi.js):
-
CRUD ??: ??? API?? ???? ????(????, ??, ????, ??)?? ?? ?????? ???? ????.
- getPosts: ???? ??? ???? ?????. ?, ? ?? ??(???)? ???? ???? ??? ?? ??? ??????.
- createPost, updatePost ? deletePost: ?? ?? ??(??? ??, ???? ?? ??)? ?????.
- ??? ??: ? ?????? ??? ?????(?: { type: 'Posts', id }) ?? ??? ? ?? ??? ???? ?????. ?? ??, ???? ????? ???? ??? ????? ?? ?? ?? getPosts? ??? ???? ????? ???? ?????.
-
CRUD ??: ??? API?? ???? ????(????, ??, ????, ??)?? ?? ?????? ???? ????.
// src/store/store.js import AsyncStorage from '@react-native-async-storage/async-storage'; import { combineReducers, configureStore, isRejectedWithValue } from '@reduxjs/toolkit'; import { setupListeners } from '@reduxjs/toolkit/query'; import { FLUSH, PAUSE, PERSIST, persistReducer, PURGE, REGISTER, REHYDRATE } from 'redux-persist'; import { authApi } from '../api/authApi'; import { postsApi } from '../api/postsApi'; import { usersApi } from '../api/usersApi'; import authSlice from '../features/auth/authSlice'; const persistConfig = { key: 'root', version: 1, storage: AsyncStorage, blacklist: ['auth', postsApi.middleware, usersApi.middleware, authApi.middleware], // these reduce will not persist data (NOTE: blacklist rtk api slices so that to use tags) // whitelist: ['users'], //these reduce will persist data }; const getEnhancers = (getDefaultEnhancers) => { if (process.env.NODE_ENV === 'development') { const reactotron = require('../reactotronConfig/ReactotronConfig').default; return getDefaultEnhancers().concat(reactotron.createEnhancer()); } return getDefaultEnhancers(); }; /** * On api error this will be called */ export const rtkQueryErrorLogger = (api) => (next) => (action) => { // RTK Query uses `createAsyncThunk` from redux-toolkit under the hood, so we're able to utilize these matchers! if (isRejectedWithValue(action)) { console.log('isRejectedWithValue', action.error, action.payload); alert(JSON.stringify(action)); // This is just an example. You can replace it with your preferred method for displaying notifications. } return next(action); }; const reducer = combineReducers({ auth: authSlice, [postsApi.reducerPath]: postsApi.reducer, [usersApi.reducerPath]: usersApi.reducer, [authApi.reducerPath]: authApi.reducer, }); const persistedReducer = persistReducer(persistConfig, reducer); const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], }, }).concat(postsApi.middleware, usersApi.middleware, authApi.middleware, rtkQueryErrorLogger), enhancers: getEnhancers, }); setupListeners(store.dispatch); export default store;
-
usersApi(src/api/usersApi.js):
- ? API? ??? ???? ???? ??? Redux? ??? ???? Authorization ??? ?????.
- ??: prepareHeaders? ??? ?? ?? ??? ????? ???? ???? ???? ??? API ??? ?????.
3. ?? ????(src/features/auth/authSlice.js)
// src/api/authApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { setToken } from '../features/auth/authSlice'; export const authApi = createApi({ reducerPath: 'authApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com/auth/', }), endpoints: (builder) => ({ login: builder.mutation({ query: (credentials) => ({ url: 'login', method: 'POST', body: credentials, }), async onQueryStarted(arg, { dispatch, queryFulfilled }) { try { const { data } = await queryFulfilled; dispatch(setToken(data.accessToken)); // Store the token in Redux } catch (error) { console.error('Login error:', error); } }, }), }), }); export const { useLoginMutation } = authApi;
- authSlice: Redux ????? ?? ??(? ?? ??? ??)? ?????.
- ?? ??: authSlice? ??? API ?????? ????? ? ???? ??? ??? ?????.
-
??:
- setToken: ?? ??? Redux ??? ?????.
- logout: Redux?? ??? ?? ???? ????? ????????.
4. ???? ?? Reactotron(src/reactotronConfig/ReactotronConfig.js)
// src/api/postsApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; // Define the postsApi slice with RTK Query export const postsApi = createApi({ // Unique key for the API slice in Redux state reducerPath: 'postsApi', // Configure base query settings, including the base URL for all requests baseQuery: fetchBaseQuery({ baseUrl: 'https://jsonplaceholder.typicode.com', }), // Define cache tag types for automatic cache invalidation tagTypes: ['Posts'], // Define API endpoints (queries and mutations) endpoints: (builder) => ({ // Query to fetch a paginated list of posts getPosts: builder.query({ // URL and parameters for paginated posts query: ({ page = 1, limit = 10 }) => `/posts?_page=${page}&_limit=${limit}`, // Tagging posts to automatically refresh this cache when needed providesTags: (result) => result ? [...result.map(({ id }) => ({ type: 'Posts', id })), { type: 'Posts', id: 'LIST' }] : [{ type: 'Posts', id: 'LIST' }], }), // Query to fetch a single post by its ID getPostById: builder.query({ // Define query with post ID in the URL path query: (id) => `/posts/${id}`, // Tag individual post by ID for selective cache invalidation providesTags: (result, error, id) => [{ type: 'Posts', id }], }), // Mutation to create a new post createPost: builder.mutation({ // Configure the POST request details and payload query: (newPost) => ({ url: '/posts', method: 'POST', body: newPost, }), // Invalidate all posts (paginated list) to refresh after creating a post invalidatesTags: [{ type: 'Posts', id: 'LIST' }], }), // Mutation to update an existing post by its ID updatePost: builder.mutation({ // Define the PUT request with post ID and updated data in the payload query: ({ id, ...updatedData }) => ({ url: `/posts/${id}`, method: 'PUT', body: updatedData, }), // Invalidate cache for both the updated post and the paginated list invalidatesTags: (result, error, { id }) => [ { type: 'Posts', id }, { type: 'Posts', id: 'LIST' }, ], }), // Mutation to delete a post by its ID deletePost: builder.mutation({ // Define the DELETE request with post ID in the URL path query: (id) => ({ url: `/posts/${id}`, method: 'DELETE', }), // Invalidate cache for the deleted post and the paginated list invalidatesTags: (result, error, id) => [ { type: 'Posts', id }, { type: 'Posts', id: 'LIST' }, ], }), }), }); // Export generated hooks for each endpoint to use them in components export const { useGetPostsQuery, // Use this when you want data to be fetched automatically as the component mounts or when the query parameters change. useLazyGetPostsQuery, // Use this when you need more control over when the query runs, such as in response to a user action (e.g., clicking a button), conditional fetching, or specific events. useGetPostByIdQuery, useCreatePostMutation, useUpdatePostMutation, useDeletePostMutation, } = postsApi;
- Reactotron: Reactotron? Redux ?? ??? ????, API ??? ??????, ??? ???? ? ??? ?? ??? ?????.
- ??: console.log ?? ? Redux ??? ????? ?????. ?? ???? ? ??? ?? ??? ????? ???? ??? ???? ?? ???? ? ?? ??? ??? ?????.
5. ?? ?????? ????
// src/api/usersApi.js import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const usersApi = createApi({ reducerPath: 'usersApi', baseQuery: fetchBaseQuery({ baseUrl: 'https://dummyjson.com', prepareHeaders: (headers, { getState }) => { // Get the token from the Redux auth state const { token } = getState().auth; // If the token exists, set it in the Authorization header if (token) { headers.set('Authorization', `Bearer ${token}`); } // Optional: include credentials if needed by the API headers.set('credentials', 'include'); return headers; }, }), endpoints: (builder) => ({ // Fetch user profile with token in Authorization header getUserProfile: builder.query({ query: () => '/auth/me', }), }), }); export const { useGetUserProfileQuery } = usersApi;
-
? ?? ??(src/App.js):
- App ?? ??? ?? ??????? Provider(Redux? ?? ???? ??? ??) ? PersistGate(?? ??? ??? ??? ???? ???? ??)? ?????.
- PersistGate? ?? ???? ?? ???? ??? ??? ???? ?? ?? ???? ????.
// src/MainApp.js import React, { useEffect, useState } from 'react'; ?? { ?????, ??, ?????, ??, ???????, ?????, ???, ??? ??, ??, } '?? ????'??; 'react-native-safe-area-context'?? { SafeAreaView }? ?????. import { useDispatch, useSelector } from 'react-redux'; import { useLoginMutation } from './api/authApi'; ?? { useCreatePostMutation, useDeletePostMutation, useGetPostsQuery, useLazyGetPostsQuery, useUpdatePostMutation, } './api/postsApi'??; import { useGetUserProfileQuery } from './api/usersApi'; ???? { ???? }'./features/auth/authSlice'??; const MainApp = () => { const [newPostTitle, setNewPostTitle] = useState(''); const [???, setPage] = useState(1); const [postsData, setPostsData] = useState([]); const [????, setRefreshing] = useState(false); const [isModalVisible, setModalVisible] = useState(false); const ???? = useDispatch(); const ?? = useSelector((state) => state.auth.token); // ??? ???? const [login, { isLoading: isLoggingIn }] = useLoginMutation(); // ??? ??? ? ?? ? ??? ???? ?????. const { ???: userProfile, ?? ????: refetchUserProfile } = useGetUserProfileQuery(???? ??, { ????: !??, }); // ???? ??? ???? ?????. const { ???: ???, ?? ?, ???? ?, isError, ?? ????, } = useGetPostsQuery({ ???, ??: 10 }); // useQuery ??? ?? ?? ? ???? ????? ??? ?????. ?? ?? ??? ???? ??? ???? ?????. // ??? 1? ?? ???? ?? ???? ?? ??? ?????. const [triggerFetchFirstPage, { data:lazyData }] = useLazyGetPostsQuery(); // useLazyquery? ?? ??? ?? API ??? ????? ??? ?????. const [createPost] = useCreatePostMutation(); const [updatePost] = useUpdatePostMutation(); const [deletePost] = useDeletePostMutation(); useEffect(() => { if (???) { setPostsData((prevData) => (??? === 1 ? ???: [...prevData, ...???])); } }, [???, ???]); // ??? ??? const handlerLogin = async () => { ???? { const ?? ?? = { ??? ??: 'emilys', ????: 'emilyspass' }; ???? ?????(?? ??); console.log('userProfile', userProfile); refetchUserProfile(); } ??(??) { console.error('??? ??:', error); } }; const handlerRefresh = async () => { setRefreshing(true); setPage(1); // ?? ???? ?? ???? 1? ??????. setPostsData([]); // ??? ??? ?? ???? ????. // ? ?? ??? ????? ????? ??????. const { data } = TriggerFetchFirstPage({ ???: 1, ??: 10 })? ?????. if (???) { setPostsData(???); // ??? ???? ? ?? ???? ??? ?????. } setRefreshing(false); }; // ? ???? ???? ??? ???? ??? ?? ?????. const handlerCreatePost = async () => { if (newPostTitle) { const { data: newPost } = createPost({ title: newPostTitle, body: '? ??? ???' })? ???? ????. setNewPostTitle(''); setPostsData((prevData) => [newPost, ...prevData]); ?? ????(); } }; // ?? ???? ?????? ??? "HASAN"? ?????. const handlerUpdatePost = ???(???) => { const { data:updatePost } = updatePost? ?????({ id: post.id, ??: `${post.title} ??`, }); setPostsData((prevData) => prevData.map((item) => (item?.id ===updatePost?.id ?updatePost : item)) ); }; // ???? ???? UI?? ?? ?????. const handlerDeletePost = async (id) => { deletePost(id)? ?????; setPostsData((prevData) => prevData.filter((post) => post.id !== id)); }; // ?? ???? ?? ? ?? ??? ?? const loadMorePosts = () => { if (!isFetching) { setPage((prevPage) => prevPage 1); } }; // ?? ??? ?? const ???? = () => { setModalVisible(!isModalVisible); }; if (isLoading && page === 1) return <Text>Loading...</Text> if (isError) return <Text>??? ???? ??.</Text> ?? ( <SafeAreaView> <ul> <li> <strong>MainApp ?? ??(src/MainApp.js)</strong>: <ul> <li> <strong>?? ? ??</strong>: ?? ??(?: ??? ??? ??) ? useLoginMutation? ?? ??? ???? ?? ???? ?? ??? ??????.</li> <li> <strong>???</strong>: <ul> <li>useLoginMutation? ???? ???? ???? ?? refetchUserProfile? ????? ??? ??? ???? ?????.</li> <li> <em>??? ??</em>: ??? ??? ??? ?? ??? ???? ?????(????: !token) ???? API ??? ????.</li> </ul> </li> <li> <strong>??? ???? ?</strong>: <ul> <li>useGetPostsQuery? ???? ???? ??? ???? ???? ???? ???? ? ? ?? ???? ??? ?? ???? ?????.</li> <li> <em>???? ??</em>: ???? ??? ??? ?? ?? ? ???, ????? ??? ?? ?? ??? ?????.</li> </ul> </li> <li> <strong>??? ??, ????, ??</strong>: <ul> <li> <em>Create</em>: createPost? ???? ??? ??? ??? ? ???? ?? ???????.</li> <li> <em>????</em>: ???? ? ??? ??? "HASAN"? ?????.</li> <li> <em>??</em>: deletePost? ?? ??? ??? ???? ?? ??? ?? ?? ???? ???? UI? ???????.</li> </ul> </li> <li> <strong>UI ??</strong>: <ul> <li>??? ??? ???? ?????. ??? ??? userProfile ???? ??? ???? ????? ??? ??? ?????.</li> </ul> </li> <li> <strong>FlatList</strong>: ???? ??? ???? ???? ??? ???? ???? ???? ????.</li> </ul> </li> </ul> <hr> <h2> ??: </h2> <p>React Native ?? ???? ??? ?? ? API ?? ??? ?? <strong>Redux Toolkit(RTK) ??</strong>? ?????. ???? ??? ?????.</p> <ol> <li><p><strong>??? ??</strong>: ? ?? ???? ?? ???? ???? ?? redux-persist, ?? ??? ?? ??? ????, ?? ???? ???? ?? Reactotron? ?? Redux ???.</p></li> <li> <p><strong>RTK ??? ??? API</strong>:</p><ul> <li> <strong>authApi</strong>? ??? ???? ??? ???? ??? Redux? ?????.</li> <li> <strong>postsApi</strong>? ???? ??, ???? ?? ??? ? ?? ??? ???? ???? ???? ?? ??? ???? ?? CRUD ??? ?????.</li> <li> <strong>usersApi</strong>? ?? ?? ?? ?? ??? ???? ??? ???? ?????.</li> </ul> </li> <li><p><strong>Auth Slice</strong>: ?? ??? ???? ???/???? ? ??? ????? ???? ??? ?????.</p></li> <li> <p><strong>? ? MainApp ????</strong>:</p> <ul> <li>?? ?? Provider ? PersistGate? ?? ??? ???? ??? ?? ??? ????? ???.</li> <li> MainApp? ??? ????, ??, ???? ? ??? ?????. ???? ???? ????(?: ??? ??? ?? ??? ??? ????) ??? ?? ? ?? ???? ?????. </li> <li>???? ??? ??? ???? FlatList? ????, ????? ??? ????, ???? ??? ????? ?? ?? ???? ?????.</li> </ul> </li> </ol> <blockquote> <p>?? ??->
? ??? RTK ??? ??? React Native? ???? ??? ??? ?? ?????. ??? ??? PHP ??? ????? ?? ?? ??? ?????!

? AI ??

Undress AI Tool
??? ???? ??

Undresser.AI Undress
???? ?? ??? ??? ?? AI ?? ?

AI Clothes Remover
???? ?? ???? ??? AI ?????.

Clothoff.io
AI ? ???

Video Face Swap
??? ??? AI ?? ?? ??? ???? ?? ???? ??? ?? ????!

?? ??

??? ??

???++7.3.1
???? ?? ?? ?? ???

SublimeText3 ??? ??
??? ??, ???? ?? ????.

???? 13.0.1 ???
??? PHP ?? ?? ??

???? CS6
??? ? ?? ??

SublimeText3 Mac ??
? ??? ?? ?? ?????(SublimeText3)

??? ??











Java ? JavaScript? ?? ?? ????? ??? ?? ?? ?? ???? ????? ?????. Java? ??? ? ??? ?????? ??? ???? JavaScript? ?? ? ??? ??? ?????.

JavaScriptCommentsareEnsentialformaining, ?? ? ???? 1) Single-LinecommentsERUSEDFORQUICKEXPLANATIONS.2) Multi-linecommentSexplaincleClexLogicOrprovidedEdeDDocumentation.3) inlineecommentsClarifySpecificPartSofcode.bestPractic

JavaScript?? ??? ??? ?? ? ? ?? ??? ???????. 1. ?? ??? ??? ???? ?? ??? ????. ISO ?? ???? ???? ???? ???? ?? ????. 2. ?? ??? ?? ???? ??? ?? ???? ??? ? ??? ? ?? 0?? ????? ?? ??????. 3. ?? ?? ???? ???? ???? ?? ?????? ??? ? ????. 4. Luxon? ?? ???? ???? ?????? ???? ?? ????. ??? ?? ???? ????? ???? ??? ????? ?? ? ????.

TAGGSATTHEBOTTOMOFABLOGPOSTORWEBPAGESERVESPRACTICALPURSEO, USEREXPERIENCE, andDESIGN.1.ITHELPSWITHEOBYOWNSESPORENGENSTOESTOCESKESKERKESKERKERKERDER-RELEVANTTAGSWITHOUTHINGTEMAINCONTENT.2.ITIMPROVESEREXPERKEEPINGTOPONTEFOCUSOFOFOFOCUSOFOFOFOCUCUSONTHEATECLL

JavaScriptIspreferredforwebDevelopment, whithjavaisbetterforlarge-scalebackendsystemsandandandoidapps.1) javascriptexcelsincreatinginteractivewebexperiences withitsdynatureanddommanipulation.2) javaoffersstrongtypingandobject-Orientededededededededededededededededdec

javascriptassevenfundamentalDatatatypes : ??, ???, ??, unull, ??, ? symbol.1) ?? seAdouble-precisionformat, ??? forwidevaluerangesbutbecautiouswithfatingfointarithmetic.2) stringsareimmutable, useefficientconcatenationmethendsf

??? ?? ? ??? DOM?? ??? ??? ? ?????. ??? ?? ????? ?? ??????, ??? ?? ???? ?? ????????. 1. ??? ??? addeventListener? usecapture ?? ??? true? ???? ?????. 2. ??? ??? ?? ???? usecapture? ???? ????? ?????. 3. ??? ??? ??? ??? ???? ? ??? ? ????. 4. ??? ?? ?? ?? ??? ?? ??? ??????? ??? ???? ?????. 5. ??? ?? ?? ?? ??? ?? ???? ?? ???? ? ??? ? ????. ? ? ??? ???? ???? JavaScript? ??? ??? ??? ????? ???? ???? ??? ??????.

Java? JavaScript? ?? ????? ?????. 1. Java? ???? ???? ??? ? ??? ?????? ?????? ? ?? ???? ?????. 2. JavaScript? ?? ? ?? ?? ? ??? ?? ??? ???? ??? ? ?? ? ?? ?????.
