in React Native
Deputy CTO - Theodo UK
https://twitter.com/WoodyRousseau
https://github.com/wrousseau
Helping our clients seize the opportunity of the digital transformation since 2009
for App Usages
12% - Dentist Waiting Room - Poor 3G
for App Usages
12% - Dentist Waiting Room - Poor 3G
27% - Your Office's toilets - Great Wifi
for App Usages
12% - Dentist Waiting Room - Poor 3G
27% - Your Office's toilets - Great Wifi
29% - Awful Tinder date at restaurant - Good 4G
for App Usages
12% - Dentist Waiting Room - Poor 3G
27% - Your Office's toilets - Great Wifi
29% - Awful Tinder date at restaurant - Good 4G
78% - The Subway - ?
Implementing each feature for 3 states
Defensive Design
Decreasing the probability for a user to encounter loading and blank states
Server
Data
Redux Store
// request the boards
export const boardsRequest = (state: Object) => state.merge({ error: false, fetching: true })
}
// successful boards lookup
export const boardsSuccess = (state: Object, action: Object) => {
return state.merge({ fetching: false, error: false, boards: action.boards });
};
// failed to get the boards
export const boardsFailure = (state: Object) => state.merge({ error: true, fetching: false })
Server
Data
Redux Store
AsyncStorage
Persist
Rehydrate
AsyncStorage
Limit: Your device space on iOS, 6MB by default on Android
const datingApps = [
{ id: 1, name: 'Tinder' },
{ id: 2, name: 'Bumble' },
];
const datingApps = {
data: [
{ id: 1, name: 'Tinder' },
{ id: 2, name: 'Bumble' },
],
fetching: false
};
const manifest = {
1: (state) => state
2: (state) => ({ ...state, datingApps: { data: state.datingApps, fetching: false }}),
};
Current manifest version
redux-persist-migrate
AsyncStorage
import { NetInfo } from 'react-native';
function handleConnectivityChange(isConnected) {
// Dispatch action to change the status in the state
}
NetInfo.isConnected.addEventListener(
'change',
handleFirstConnectivityChange
);
react-native-dropdown-alert
redux-optimist
BLACK_REQUEST
BLUE_REQUEST
BLACK_ROLLBACK
BLUE_COMMIT
Redux Store
1
2
3
4
5
Jani Eväkallio
const followUser = userId => ({
type: 'FOLLOW_USER_REQUEST',
payload: { userId },
meta: {
offline: {
// the network action to execute:
effect: { url: '/api/follow', method: 'POST', body: { userId } },
// action to dispatch when effect succeeds:
commit: { type: 'FOLLOW_USER_COMMIT', meta: { userId } },
// action to dispatch if network action fails permanently:
rollback: { type: 'FOLLOW_USER_ROLLBACK', meta: { userId } }
}
}
});
const followingUsersReducer = (state, action) {
switch(action.type) {
case 'FOLLOW_USER':
return { ...state, [action.payload.userId]: true };
case 'FOLLOW_USER_ROLLBACK':
return omit(state, [action.payload.userId]);
default:
return state;
}
}
Strategy:
export function * handleNetworkRequiringAction (action) {
const onlineStatus = yield select(state => state.offline.online);
if (!onlineStatus) {
yield put({ type: 'SHOW_CONNEXION_ALERT' });
const { connexionRestored, timeout } = yield race({
connexionRestored: take(action => {
return action.type === 'Offline/STATUS_CHANGED' && action.payload.online;
}),
timeout: call(delay, 4000),
});
yield put({ type: 'HIDE_CONNEXION_ALERT' });
}
}
export function * watchNetworkRequiringActions () {
while (true) {
const action = yield take([
BoardsTypes.SUBSCRIBE_TO_BOARD_REQUEST,
BoardsTypes.UNSUBSCRIBE_TO_BOARD_REQUEST,
]);
yield call(handleNetworkRequiringAction, action);
yield call(delay, 6000);
}
}
@connect(state => ({
connexionAlert: state.connexionAlert,
}), mapDispatchToProps)
export default class BoardsScreen extends React.Component {
componentWillReceiveProps(nextProps) {
if (this.dropdown) {
if (!this.props.connexionAlert && nextProps.connexionAlert) {
this.dropdown.alertWithType('info', 'Info', 'Offline device');
} else if (this.props.connexionAlert && !nextProps.connexionAlert) {
this.dropdown.dismiss();
}
}
}
render() {
return (
<View>
...
<DropdownAlert
ref={(ref) => this.dropdown = ref}
/>
</View>
);
}
};
react-redux-saga-offline in preparation ;)
https://twitter.com/WoodyRousseau
What if there is a conflict for PUT and PATCH requests?
BACK-END DEVELOPER ???
id | text | createdAt | updatedAt | updateAttemptedAt |
---|---|---|---|---|
1 | John | 2017-01-01 | 2017-01-04 | 2017-01-03 |
2 | Philip | 2017-02-01 | 2017-02-04 | 2017-02-04 |
DB
F1
F2
F3
UA3
UA2
UA1
U1
U3
U2
https://github.com/theodo-UK/offline-goodies
4 strategies
The idea: reject with a 409 if the update is rejected
const rejectLaterAttemptedUpdate = (options = {}) => (hook) => {
const updateAttemptDateLabel = options.updateAttemptDateLabel || 'updateAttemptedAt';
return hook.app.service(hook.path).get(hook.id)
.then(serverObject => {
if (serverObject[updateAttemptDateLabel] < new Date(hook.data[updateAttemptDateLabel])) {
const error = new errors.Conflict('There was a earlier modify attempt');
return Promise.reject(error);
}
return Promise.resolve(hook);
});
};
Woody Rousseau - woodyr@theodo.co.uk