Skip to content

Commit

Permalink
recoil Integration and redux removal
Browse files Browse the repository at this point in the history
  • Loading branch information
Shamoil authored and Shamoil committed Aug 28, 2024
1 parent 9c4aa4d commit ec198a5
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 94 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ module.exports = {
}
],
'fp/no-nil': 0,
'fp/no-unused-expression': 0
'fp/no-unused-expression': 0,
'fp/no-throw': 0
},
settings: {
'import/resolver': {
Expand Down
22 changes: 8 additions & 14 deletions app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,16 @@ import { I18nextProvider } from 'react-i18next';
import LanguageProvider from '@atoms/LanguageProvider';
import RootScreen from '@scenes/RootScreen';
import i18n from '@app/i18n';
import createStore from '@app/rootReducer';

import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
import 'react-native-gesture-handler';
const { store, persistor } = createStore();
import { RecoilRoot } from 'recoil';
const App = () => (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<I18nextProvider i18n={i18n}>
<LanguageProvider>
<RootScreen />
</LanguageProvider>
</I18nextProvider>
</PersistGate>
</Provider>
<RecoilRoot>
<I18nextProvider i18n={i18n}>
<LanguageProvider>
<RootScreen />
</LanguageProvider>
</I18nextProvider>
</RecoilRoot>
);

export default App;
2 changes: 1 addition & 1 deletion app/config/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SIMPSONS_API } from '@env';

export const Config = {
API_URL: SIMPSONS_API
API_URL: 'https://thesimpsonsquoteapi.glitch.me/'
};
5 changes: 4 additions & 1 deletion app/navigators/appNavigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ const Stack = createStackNavigator();
export default function AppNavigator() {
return (
<NavigationContainer ref={setTopLevelNavigator}>
<Stack.Navigator headerMode="none" initialRouteName="SplashScreen">
<Stack.Navigator
screenOptions={{ headerShown: false }}
initialRouteName="SplashScreen"
>
<Stack.Screen name="SplashScreen" component={SplashScreen} />
<Stack.Screen name="MainScreen" component={ExampleScreen} />
</Stack.Navigator>
Expand Down
104 changes: 52 additions & 52 deletions app/scenes/ExampleScreen/index.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import { Button, Platform, View, ActivityIndicator } from 'react-native';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { PropTypes } from 'prop-types';
import React, { useEffect } from 'react';
import { Button, Platform, View, ActivityIndicator, Text } from 'react-native';
import {
useRecoilState,
useSetRecoilState,
useRecoilValue,
useRecoilValueLoadable
} from 'recoil';
import styled from 'styled-components/native';
import { createStructuredSelector } from 'reselect';
import { injectIntl } from 'react-intl';
import React, { useEffect } from 'react';

import AppContainer from '@atoms/Container';
import SimpsonsLoveWednesday from '@organisms/SimpsonsLoveWednesday';

import {
selectUser,
selectUserIsLoading,
selectUserErrorMessage
} from './selectors';
import { exampleScreenActions } from './reducer';

/**
* This is an example of a container component.
*
* This screen displays a little help message and informations about a fake user.
* Feel free to remove it.
*/
userState,
userIsLoadingState,
userErrorMessageState,
fetchUserSelector,
fetchTriggerState
} from './recoilState';
import If from '@app/components/atoms/If';
import { conditionalOperatorFunction } from '@app/utils/common';

const Container = styled(AppContainer)`
margin: 30px;
Expand All @@ -38,56 +36,58 @@ const CustomButtonParentView = styled(View)`
max-width: 80px;
align-self: center;
`;

const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\nCmd+D or shake for dev menu.',
android:
'Double tap R on your keyboard to reload,\nShake or press menu button for dev menu.'
});

const ExampleScreen = props => {
const ExampleScreen = () => {
const [user, setUser] = useRecoilState(userState);
const setFetchTrigger = useSetRecoilState(fetchTriggerState);
const userLoadable = useRecoilValueLoadable(fetchUserSelector);

const requestFetchUser = () => {
props.fetchUser();
setFetchTrigger(prev => prev + 1);
};

useEffect(() => {
requestFetchUser();
}, []);

useEffect(() => {
if (userLoadable.state === 'hasValue') {
setUser(userLoadable.contents);
}
}, [userLoadable?.contents?.character]);

return (
<Container>
{props.userIsLoading ? (
<If
condition={userLoadable.state === 'loading'}
otherwise={
<View testID="example-container-content">
<SimpsonsLoveWednesday
instructions={instructions}
userErrorMessage={conditionalOperatorFunction(
userLoadable.state === 'hasError',
userLoadable.contents.message,
null
)}
user={user}
/>
<CustomButtonParentView>
<Button onPress={requestFetchUser} title="Refresh" />
</CustomButtonParentView>
</View>
}
>
<ActivityIndicator testID="loader" size="large" color="#0000ff" />
) : (
<View testID="example-container-content">
<SimpsonsLoveWednesday
instructions={instructions}
userErrorMessage={props.userErrorMessage}
user={props.user}
/>
<CustomButtonParentView>
<Button onPress={requestFetchUser} title="Refresh" />
</CustomButtonParentView>
</View>
)}
</If>
</Container>
);
};

ExampleScreen.propTypes = {
user: PropTypes.object,
userIsLoading: PropTypes.bool,
userErrorMessage: PropTypes.string,
fetchUser: PropTypes.func
};

const mapStateToProps = createStructuredSelector({
user: selectUser(),
userIsLoading: selectUserIsLoading(),
userErrorMessage: selectUserErrorMessage()
});

const mapDispatchToProps = dispatch => ({
fetchUser: () => dispatch(exampleScreenActions.requestFetchUser())
});

const withConnect = connect(mapStateToProps, mapDispatchToProps);
export default compose(withConnect)(ExampleScreen);
export default ExampleScreen;
export { ExampleScreen as ExampleScreenTest };
43 changes: 43 additions & 0 deletions app/scenes/ExampleScreen/recoilState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { atom, selector } from 'recoil';
import { getUser } from '@app/services/userService';
import { Errors } from '@app/utils/erros';

// Atom to manage user state
export const userState = atom({
key: 'userState',
default: null
});

// Atom to manage loading state
export const userIsLoadingState = atom({
key: 'userIsLoadingState',
default: false
});

// Atom to manage error messages
export const userErrorMessageState = atom({
key: 'userErrorMessageState',
default: null
});

// Atom to trigger a fetch
export const fetchTriggerState = atom({
key: 'fetchTriggerState',
default: 0 // This will be incremented to trigger re-fetching
});

// Selector to fetch user data
export const fetchUserSelector = selector({
key: 'fetchUserSelector',
get: async ({ get }) => {
get(fetchTriggerState); // Read the trigger state to force re-fetch

const response = await getUser();
if (response.ok) {
const { data } = response;

return data[0];
}
throw new Error(Errors.USER_FETCH_ERROR);
}
});
32 changes: 14 additions & 18 deletions app/scenes/RootScreen/index.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import { connect } from 'react-redux';
import { PropTypes } from 'prop-types';
import React, { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { appState } from './recoilState';
import AppNavigator from '@navigators/appNavigator';
import Container from '@atoms/Container';
import React, { useEffect } from 'react';
import { setTopLevelNavigator } from '@app/services/navigationService';
import { rootScreenActions } from './reducer';
import { navigateAndReset } from '@app/services/navigationService';

const RootScreen = () => {
const app = useRecoilValue(appState);

const RootScreen = props => {
useEffect(() => {
// Run the startup function when the component mounts
props.startup();
}, []);
// Simulate startup effect
if (!app) {
setTimeout(() => navigateAndReset('MainScreen'), 1000);
}
}, [app]);

return (
<Container testID="root-screen">
<AppNavigator ref={setTopLevelNavigator} />
<AppNavigator />
</Container>
);
};

RootScreen.propTypes = {
startup: PropTypes.func
};

const mapDispatchToProps = dispatch => ({
startup: () => dispatch(rootScreenActions.startup())
});
export default connect(null, mapDispatchToProps)(RootScreen);
export default RootScreen;
export { RootScreen as RootScreenTest };
6 changes: 6 additions & 0 deletions app/scenes/RootScreen/recoilState.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom } from 'recoil';

export const appState = atom({
key: 'appState',
default: null
});
3 changes: 3 additions & 0 deletions app/utils/erros.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const Errors = {
USER_FETCH_ERROR: "'There was an error while fetching user information.'"
};
14 changes: 13 additions & 1 deletion ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,12 @@ PODS:
- glog
- RCT-Folly (= 2022.05.16.00)
- React-Core
- RNSentry (5.30.0):
- hermes-engine
- React-Core
- React-hermes
- Sentry/HybridSDK (= 8.34.0)
- Sentry/HybridSDK (8.34.0)
- SocketRocket (0.6.1)
- Yoga (1.14.0)

Expand Down Expand Up @@ -1183,12 +1189,14 @@ DEPENDENCIES:
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- "RNSentry (from `../node_modules/@sentry/react-native`)"
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

SPEC REPOS:
trunk:
- fmt
- libevent
- Sentry
- SocketRocket

EXTERNAL SOURCES:
Expand Down Expand Up @@ -1319,6 +1327,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
RNSentry:
:path: "../node_modules/@sentry/react-native"
Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"

Expand Down Expand Up @@ -1388,8 +1398,10 @@ SPEC CHECKSUMS:
RNGestureHandler: 96439cf6543defdde87459e48cd1a3f0e45a008e
RNReanimated: ba95f6d26ca4b8a8f7f2b15f62a3513750762f6b
RNScreens: b6b64d956af3715adbfe84808694ae82d3fec74f
RNSentry: 038afe562a53fa0ec791e1cd9fb66239c77a2818
Sentry: 1183df2306eea74ff0564c4bb1c2577ccd15d054
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
Yoga: d17d2cc8105eed528474683b42e2ea310e1daf61
Yoga: 805bf71192903b20fc14babe48080582fee65a80

PODFILE CHECKSUM: 71a7eba1109c7de188217c03cf1824ab1d487aec

Expand Down
Loading

0 comments on commit ec198a5

Please sign in to comment.