From dae1ea0f53d75e34fd7208279c450689afed7afb Mon Sep 17 00:00:00 2001 From: jhen Date: Fri, 21 Jul 2023 12:41:18 +0800 Subject: [PATCH] Update redux example by follow official examples --- .../test-old-bridge/examples/redux/App.js | 6 +- .../examples/redux/app/store.js | 8 + .../examples/redux/configureStore.js | 14 -- .../examples/redux/containers/Counter.js | 70 -------- .../redux/features/counter/Counter.js | 56 +++++++ .../redux/features/counter/counterAPI.js | 6 + .../redux/features/counter/counterSlice.js | 73 ++++++++ examples/test-old-bridge/package-lock.json | 158 +++++++++++------- examples/test-old-bridge/package.json | 7 +- 9 files changed, 242 insertions(+), 156 deletions(-) create mode 100644 examples/test-old-bridge/examples/redux/app/store.js delete mode 100644 examples/test-old-bridge/examples/redux/configureStore.js delete mode 100644 examples/test-old-bridge/examples/redux/containers/Counter.js create mode 100644 examples/test-old-bridge/examples/redux/features/counter/Counter.js create mode 100644 examples/test-old-bridge/examples/redux/features/counter/counterAPI.js create mode 100644 examples/test-old-bridge/examples/redux/features/counter/counterSlice.js diff --git a/examples/test-old-bridge/examples/redux/App.js b/examples/test-old-bridge/examples/redux/App.js index 6ee2f16c..e8f612dd 100644 --- a/examples/test-old-bridge/examples/redux/App.js +++ b/examples/test-old-bridge/examples/redux/App.js @@ -1,9 +1,7 @@ import React from 'react'; import { Provider } from 'react-redux'; -import Counter from './containers/Counter'; -import configureStore from './configureStore'; - -const store = configureStore(); +import { Counter } from './features/counter/Counter'; +import { store } from './app/store'; export default () => ( diff --git a/examples/test-old-bridge/examples/redux/app/store.js b/examples/test-old-bridge/examples/redux/app/store.js new file mode 100644 index 00000000..9eca6d24 --- /dev/null +++ b/examples/test-old-bridge/examples/redux/app/store.js @@ -0,0 +1,8 @@ +import { configureStore } from '@reduxjs/toolkit'; +import counterReducer from '../features/counter/counterSlice'; + +export const store = configureStore({ + reducer: { + counter: counterReducer, + }, +}); diff --git a/examples/test-old-bridge/examples/redux/configureStore.js b/examples/test-old-bridge/examples/redux/configureStore.js deleted file mode 100644 index a9cdf1a7..00000000 --- a/examples/test-old-bridge/examples/redux/configureStore.js +++ /dev/null @@ -1,14 +0,0 @@ -/* eslint global-require: 0 */ -import { createStore, applyMiddleware } from 'redux'; -import { composeWithDevTools } from '@redux-devtools/extension'; -import thunk from 'redux-thunk'; -import reducer from './reducers'; - -const middlewares = [thunk]; -const enhancer = composeWithDevTools({ - // Options:https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md -})(applyMiddleware(...middlewares)); - -export default function configureStore(initialState) { - return createStore(reducer, initialState, enhancer); -} diff --git a/examples/test-old-bridge/examples/redux/containers/Counter.js b/examples/test-old-bridge/examples/redux/containers/Counter.js deleted file mode 100644 index d5fd23d5..00000000 --- a/examples/test-old-bridge/examples/redux/containers/Counter.js +++ /dev/null @@ -1,70 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { StyleSheet, View, Text, TouchableHighlight } from 'react-native'; - -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import * as CounterActions from '../actions/counter'; - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#F5FCFF', - }, - title: { - marginBottom: 20, - fontSize: 25, - textAlign: 'center', - fontWeight: 'bold', - }, - text: { - fontSize: 20, - textAlign: 'center', - margin: 10, - }, -}); - -class Counter extends Component { - static propTypes = { - incrementAsync: PropTypes.func.isRequired, - increment: PropTypes.func.isRequired, - decrement: PropTypes.func.isRequired, - incrementIfOdd: PropTypes.func.isRequired, - counter: PropTypes.number.isRequired, - }; - - constructor(props) { - super(props); - const { incrementAsync } = props; - this.incrementAsync = () => incrementAsync(); - } - - render() { - const { increment, decrement, incrementIfOdd, counter } = this.props; - return ( - - Redux example - Clicked: {counter} times - - + - - - - - - - Increment if odd - - - Increment async - - - ); - } -} - -export default connect( - state => state, - dispatch => bindActionCreators(CounterActions, dispatch) -)(Counter); diff --git a/examples/test-old-bridge/examples/redux/features/counter/Counter.js b/examples/test-old-bridge/examples/redux/features/counter/Counter.js new file mode 100644 index 00000000..e0c2a6ea --- /dev/null +++ b/examples/test-old-bridge/examples/redux/features/counter/Counter.js @@ -0,0 +1,56 @@ +import React from 'react'; +import { StyleSheet, View, Text, TouchableHighlight } from 'react-native'; +import { useSelector, useDispatch } from 'react-redux'; +import { + decrement, + increment, + incrementAsync, + incrementIfOdd, + selectCount, +} from './counterSlice'; + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#F5FCFF', + }, + title: { + marginBottom: 20, + fontSize: 25, + textAlign: 'center', + fontWeight: 'bold', + }, + text: { + fontSize: 20, + textAlign: 'center', + margin: 10, + }, +}); + + +export function Counter() { + const count = useSelector(selectCount); + const dispatch = useDispatch(); + return ( + + Redux example + Clicked: {count} times + dispatch(increment())}> + + + + dispatch(decrement())}> + - + + dispatch(incrementIfOdd(count))}> + Increment if odd + + dispatch(incrementAsync())}> + Increment async + + + ); +} + +export default Counter; diff --git a/examples/test-old-bridge/examples/redux/features/counter/counterAPI.js b/examples/test-old-bridge/examples/redux/features/counter/counterAPI.js new file mode 100644 index 00000000..cc9b4a44 --- /dev/null +++ b/examples/test-old-bridge/examples/redux/features/counter/counterAPI.js @@ -0,0 +1,6 @@ +// A mock function to mimic making an async request for data +export function fetchCount(amount = 1) { + return new Promise((resolve) => + setTimeout(() => resolve({ data: amount }), 500) + ); +} diff --git a/examples/test-old-bridge/examples/redux/features/counter/counterSlice.js b/examples/test-old-bridge/examples/redux/features/counter/counterSlice.js new file mode 100644 index 00000000..8dc4b5cf --- /dev/null +++ b/examples/test-old-bridge/examples/redux/features/counter/counterSlice.js @@ -0,0 +1,73 @@ +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; +import { fetchCount } from './counterAPI'; + +const initialState = { + 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) => { + 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) => { + 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; + }); + }, +}); + +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) => 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) => (dispatch, getState) => { + const currentValue = selectCount(getState()); + if (currentValue % 2 === 1) { + dispatch(incrementByAmount(amount)); + } +}; + +export default counterSlice.reducer; diff --git a/examples/test-old-bridge/package-lock.json b/examples/test-old-bridge/package-lock.json index 738faba2..5017badd 100644 --- a/examples/test-old-bridge/package-lock.json +++ b/examples/test-old-bridge/package-lock.json @@ -8,14 +8,13 @@ "name": "test-old-bridge", "version": "1.0.0", "dependencies": { - "@redux-devtools/extension": "^3.2.5", + "@reduxjs/toolkit": "^1.9.5", "expo": "~48.0.18", "expo-status-bar": "~1.4.4", "react": "18.2.0", "react-native": "0.71.8", - "react-redux": "^7.2.9", - "redux": "^4.2.1", - "redux-thunk": "^2.3.0" + "react-redux": "^8.1.1", + "redux": "^4.2.1" }, "devDependencies": { "@babel/core": "^7.20.0" @@ -4905,16 +4904,27 @@ "resolved": "https://registry.npmjs.org/@react-native/polyfills/-/polyfills-2.0.0.tgz", "integrity": "sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==" }, - "node_modules/@redux-devtools/extension": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.2.5.tgz", - "integrity": "sha512-UhyDF7WmdnCrN1s++YC4sdQCo0z6YUnoB2eCh15nXDDq3QH1jDju1144UNRU6Nvi4inxhaIum4m9BXVYWVC1ng==", + "node_modules/@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", "dependencies": { - "@babel/runtime": "^7.20.7", - "immutable": "^4.2.2" + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" }, "peerDependencies": { - "redux": "^3.1.0 || ^4.0.0" + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } } }, "node_modules/@segment/loosely-validate-event": { @@ -5015,17 +5025,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-redux": { - "version": "7.1.25", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz", - "integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==", - "dependencies": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -5036,6 +5035,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + }, "node_modules/@types/yargs": { "version": "15.0.15", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", @@ -7905,10 +7909,14 @@ "node": ">=4.0" } }, - "node_modules/immutable": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", - "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==" + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } }, "node_modules/import-fresh": { "version": "2.0.0", @@ -11618,29 +11626,48 @@ } }, "node_modules/react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz", + "integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==", "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" }, "peerDependencies": { - "react": "^16.8.3 || ^17 || ^18" + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" }, "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, "react-dom": { "optional": true }, "react-native": { "optional": true + }, + "redux": { + "optional": true } } }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/react-refresh": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.4.3.tgz", @@ -17349,13 +17376,15 @@ "resolved": "https://registry.npmjs.org/@react-native/polyfills/-/polyfills-2.0.0.tgz", "integrity": "sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==" }, - "@redux-devtools/extension": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@redux-devtools/extension/-/extension-3.2.5.tgz", - "integrity": "sha512-UhyDF7WmdnCrN1s++YC4sdQCo0z6YUnoB2eCh15nXDDq3QH1jDju1144UNRU6Nvi4inxhaIum4m9BXVYWVC1ng==", + "@reduxjs/toolkit": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", + "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==", "requires": { - "@babel/runtime": "^7.20.7", - "immutable": "^4.2.2" + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" } }, "@segment/loosely-validate-event": { @@ -17456,17 +17485,6 @@ "csstype": "^3.0.2" } }, - "@types/react-redux": { - "version": "7.1.25", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz", - "integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==", - "requires": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - } - }, "@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -17477,6 +17495,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" }, + "@types/use-sync-external-store": { + "version": "0.0.3", + "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/yargs": { "version": "15.0.15", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", @@ -19662,10 +19685,10 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz", "integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==" }, - "immutable": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", - "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==" + "immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" }, "import-fresh": { "version": "2.0.0", @@ -22544,16 +22567,23 @@ "integrity": "sha512-1dVk9NwhoyKHCSxcrM6vY6cxmojeATsBobDicX0ZKr7DgUF2cBQRTKsimQFvzH8XhOVXyH8p4HyDSZNIFI8OlQ==" }, "react-redux": { - "version": "7.2.9", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", - "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz", + "integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==", "requires": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "dependencies": { + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + } } }, "react-refresh": { diff --git a/examples/test-old-bridge/package.json b/examples/test-old-bridge/package.json index 15f24854..9c7cf7d0 100644 --- a/examples/test-old-bridge/package.json +++ b/examples/test-old-bridge/package.json @@ -9,14 +9,13 @@ "web": "expo start --web" }, "dependencies": { - "@redux-devtools/extension": "^3.2.5", + "@reduxjs/toolkit": "^1.9.5", "expo": "~48.0.18", "expo-status-bar": "~1.4.4", "react": "18.2.0", "react-native": "0.71.8", - "react-redux": "^7.2.9", - "redux": "^4.2.1", - "redux-thunk": "^2.3.0" + "react-redux": "^8.1.1", + "redux": "^4.2.1" }, "devDependencies": { "@babel/core": "^7.20.0"