Last active
April 24, 2019 02:50
-
-
Save lancegliser/9f415690ff0e41373ede88c425cd2220 to your computer and use it in GitHub Desktop.
Publishing Redux state data through the Context API for React
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // src/contexts/UserContext/IUser.ts | |
| /** | |
| * A representation of the user returned from the Microsoft Graph api. | |
| */ | |
| export default interface IUser { | |
| "@odata.context"?: string, // "https://graph.microsoft.com/v1.0/$metadata#users/$entity" | |
| displayName: string, // "Lance Gliser" | |
| givenName?: string, // "Lance" | |
| jobTitle?: string, // "Developer" | |
| mail?: string, // "[email protected]" | |
| mobilePhone?: string, // "816-726-2730" | |
| preferredLanguage?: string, // null | |
| surname?: string, // "Gliser" | |
| userPrincipalName?: string, // "[email protected]" | |
| id?: string, // "3bbce9ce-9ff2-4b32-8dd7-dcd215cbb783" | |
| } | |
| // A method of creating an object matching all of these properties without external code having to know too much. | |
| export function getDefaultUser(): IUser { | |
| let defaultUser = {} as IUser; | |
| defaultUser.displayName = 'Guest'; | |
| return defaultUser; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // src/contexts/UserContext/IUserContext.ts | |
| import IUser from "./IUser"; | |
| export default interface IUserContext { | |
| user: IUser, | |
| login: Function, | |
| logout: Function, | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // src/contexts/UserContext/UserContextProvider.js | |
| import React, { Component } from 'react'; | |
| import { withRouter } from 'react-router-dom'; | |
| import { connect } from 'react-redux'; | |
| import UserContext from '../../contexts/UserContext'; | |
| import { | |
| clearAuthentication, | |
| updateAuthenticationToken, | |
| updateAuthenticationUser, | |
| } from '../../actions'; | |
| import AzureServices from '../../utilities/AzureServices'; | |
| import MicrosoftGraphServices from '../../utilities/MicrosoftGraphServices'; | |
| import AppLoading from '../AppLoading/AppLoading'; | |
| const mapStateToProps = state => { | |
| return { | |
| user: state.user, | |
| }; | |
| }; | |
| /** | |
| * For the moment, this can not be used with hooks if it pulls redux state and router methods. | |
| * @link https://github.com/reduxjs/react-redux/issues/1179 | |
| */ | |
| class UserContextProvider extends Component { | |
| constructor(props) { | |
| super(props); | |
| this.state = { | |
| isLoading: false, | |
| }; | |
| // A small delay to allow the variable below to be assigned before we call the callback. | |
| // This might be refactored into componentDidMount, but I'm hoping for functional refactors sooner than later. | |
| const delayedTokenReceivedCallback = ( | |
| errorDesc, | |
| token, | |
| error, | |
| tokenType | |
| ) => { | |
| setTimeout(() => { | |
| this.tokenReceivedCallback(errorDesc, token, error, tokenType); | |
| }, 5); | |
| }; | |
| this.userAgentApplication = AzureServices.getUserAgentApplication( | |
| delayedTokenReceivedCallback | |
| ); | |
| } | |
| //callback function for redirect flows | |
| tokenReceivedCallback(errorDesc, token, error, tokenType) { | |
| if (!token) { | |
| throw new Error(error + ':' + errorDesc); | |
| } | |
| switch (tokenType) { | |
| case 'id_token': | |
| this.setState({ isLoading: true }); | |
| AzureServices.getAccessToken( | |
| this.userAgentApplication, | |
| token, | |
| this.onLoginSuccess.bind(this), | |
| this.onLoginFailure.bind(this) | |
| ); | |
| break; | |
| case 'access_token': | |
| this.setState({ isLoading: true }); | |
| this.onLoginSuccess(token).bind(this); | |
| break; | |
| default: | |
| throw new Error(`Unknown token type: ${tokenType}`); | |
| } | |
| } | |
| /** | |
| * @return void | |
| */ | |
| doLogin() { | |
| const { history } = this.props; | |
| this.setState({ isLoading: true }); | |
| history.push('/'); | |
| return AzureServices.getIdToken(this.userAgentApplication); | |
| } | |
| /** | |
| * @param {String} accessToken | |
| */ | |
| onLoginSuccess(accessToken) { | |
| this.setState({ isLoading: true }); | |
| const { dispatch, history } = this.props; | |
| dispatch(updateAuthenticationToken(accessToken)); | |
| MicrosoftGraphServices.setAccessToken(accessToken) | |
| .getCurrentUser() | |
| .then(user => { | |
| dispatch(updateAuthenticationUser(user)); | |
| this.setState({ isLoading: false }); | |
| history.push('/dashboard'); | |
| }, console.error); | |
| } | |
| /** | |
| * @param error | |
| */ | |
| onLoginFailure(error) { | |
| console.log(error); | |
| } | |
| /** | |
| * Log out clearing redux stores | |
| */ | |
| doLogout() { | |
| const { dispatch, history, user } = this.props; | |
| if (!user.id) { | |
| history.push('/'); | |
| return; | |
| } | |
| dispatch(clearAuthentication()); | |
| AzureServices.logout(this.userAgentApplication); | |
| } | |
| render() { | |
| const { isLoading } = this.state; | |
| const values = { | |
| logout: this.doLogout.bind(this), | |
| login: this.doLogin.bind(this), | |
| user: this.props.user, | |
| }; | |
| return ( | |
| <UserContext.Provider value={values}> | |
| {isLoading ? <AppLoading /> : this.props.children} | |
| </UserContext.Provider> | |
| ); | |
| } | |
| } | |
| export default withRouter(connect(mapStateToProps)(UserContextProvider)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment