-
Notifications
You must be signed in to change notification settings - Fork 381
/
user-context-provider.js
141 lines (120 loc) · 4.12 KB
/
user-context-provider.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* WordPress dependencies
*/
import { createContext, useEffect, useState, useRef, useCallback, useMemo, useContext } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
/**
* External dependencies
*/
import PropTypes from 'prop-types';
/**
* Internal dependencies
*/
import { useAsyncError } from '../../utils/use-async-error';
import { Options } from '../../components/options-context-provider';
export const User = createContext();
/**
* Context provider user data.
*
* @param {Object} props Component props.
* @param {?any} props.children Component children.
* @param {string} props.userOptionDeveloperTools The key of the option to use from the settings endpoint.
* @param {string} props.userRestPath REST endpoint to retrieve options.
*/
export function UserContextProvider( { children, userOptionDeveloperTools, userRestPath } ) {
const { originalOptions, fetchingOptions } = useContext( Options );
const [ fetchingUser, setFetchingUser ] = useState( false );
const [ developerToolsOption, setDeveloperToolsOption ] = useState( null );
const [ originalDeveloperToolsOption, setOriginalDeveloperToolsOption ] = useState( null );
const [ savingDeveloperToolsOption, setSavingDeveloperToolsOption ] = useState( false );
const [ didSaveDeveloperToolsOption, setDidSaveDeveloperToolsOption ] = useState( false );
const { setAsyncError } = useAsyncError();
// This component sets state inside async functions. Use this ref to prevent state updates after unmount.
const hasUnmounted = useRef( false );
useEffect( () => () => {
hasUnmounted.current = true;
}, [] );
const hasDeveloperToolsOptionChange = useMemo(
() => null !== developerToolsOption && developerToolsOption !== originalDeveloperToolsOption,
[ developerToolsOption, originalDeveloperToolsOption ],
);
/**
* Fetch user options on mount.
*/
useEffect( () => {
if ( fetchingOptions ) {
return;
}
if ( ! originalOptions.plugin_configured ) {
setOriginalDeveloperToolsOption( null );
setDeveloperToolsOption( null );
return;
}
if ( ! userRestPath || fetchingUser || null !== originalDeveloperToolsOption ) {
return;
}
/**
* Fetches user data from the REST endpoint for the current user.
*/
( async () => {
setFetchingUser( true );
try {
const fetchedUser = await apiFetch( { path: userRestPath } );
if ( true === hasUnmounted.current ) {
return;
}
setOriginalDeveloperToolsOption( fetchedUser[ userOptionDeveloperTools ] );
setDeveloperToolsOption( fetchedUser[ userOptionDeveloperTools ] );
} catch ( e ) {
setAsyncError( e );
return;
}
setFetchingUser( false );
} )();
}, [ fetchingOptions, fetchingUser, originalDeveloperToolsOption, originalOptions.plugin_configured, setAsyncError, userOptionDeveloperTools, userRestPath ] );
/**
* Sends the option back to the REST endpoint to be saved.
*/
const saveDeveloperToolsOption = useCallback( async () => {
if ( ! hasDeveloperToolsOptionChange ) {
return;
}
setSavingDeveloperToolsOption( true );
try {
const fetchedUser = await apiFetch( { method: 'post', path: userRestPath, data: { [ userOptionDeveloperTools ]: developerToolsOption } } );
if ( true === hasUnmounted.current ) {
return;
}
setOriginalDeveloperToolsOption( fetchedUser[ userOptionDeveloperTools ] );
setDeveloperToolsOption( fetchedUser[ userOptionDeveloperTools ] );
} catch ( e ) {
setAsyncError( e );
return;
}
setDidSaveDeveloperToolsOption( true );
setSavingDeveloperToolsOption( false );
}, [ hasDeveloperToolsOptionChange, developerToolsOption, setAsyncError, userOptionDeveloperTools, userRestPath ] );
return (
<User.Provider
value={
{
developerToolsOption,
fetchingUser,
didSaveDeveloperToolsOption,
hasDeveloperToolsOptionChange,
originalDeveloperToolsOption,
saveDeveloperToolsOption,
savingDeveloperToolsOption,
setDeveloperToolsOption,
}
}
>
{ children }
</User.Provider>
);
}
UserContextProvider.propTypes = {
children: PropTypes.any,
userOptionDeveloperTools: PropTypes.string.isRequired,
userRestPath: PropTypes.string.isRequired,
};