Skip to content

Commit d763ab1

Browse files
committed
Initialize redux with redux-toolkit, refactor Login with redux
We used example generated by npx create-react-app --template redux-typescript
1 parent 2151ed6 commit d763ab1

File tree

14 files changed

+302
-95
lines changed

14 files changed

+302
-95
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"dependencies": {
66
"@inrupt/solid-client": "^1.12.0",
77
"@inrupt/solid-client-authn-browser": "^1.11.2",
8+
"@reduxjs/toolkit": "^1.6.1",
89
"@testing-library/jest-dom": "^5.11.4",
910
"@testing-library/react": "^11.1.0",
1011
"@testing-library/user-event": "^12.1.10",
@@ -19,6 +20,7 @@
1920
"@types/react": "^17.0.0",
2021
"@types/react-dom": "^17.0.0",
2122
"@types/react-modal": "^3.12.1",
23+
"@types/react-redux": "^7.1.18",
2224
"@types/styled-components": "^5.1.11",
2325
"bulma": "^0.9.3",
2426
"classnames": "^2.3.1",
@@ -35,6 +37,7 @@
3537
"react-dom": "^17.0.2",
3638
"react-markdown": "^6.0.2",
3739
"react-modal": "^3.14.3",
40+
"react-redux": "^7.2.5",
3841
"react-scripts": "4.0.3",
3942
"rehype-katex": "^5.0.0",
4043
"remark-math": "^4.0.0",

src/App.test.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1+
/* @TODO */
2+
13
import React from 'react'
2-
import { render, screen } from '@testing-library/react'
4+
import { render } from '@testing-library/react'
5+
import { Provider } from 'react-redux'
6+
import { store } from './app/store'
37
import App from './App'
48

59
test('renders learn react link', () => {
6-
render(<App />)
7-
const linkElement = screen.getByText(/learn react/i)
8-
expect(linkElement).toBeInTheDocument()
10+
const { getByText } = render(
11+
<Provider store={store}>
12+
<App />
13+
</Provider>,
14+
)
15+
16+
expect(getByText(/learn/i)).toBeInTheDocument()
917
})

src/App.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1-
import React from 'react'
1+
import React, { useEffect } from 'react'
22
import Layout from './components/Layout'
33
import DataContainer from './components/DataContainer'
4+
import { init as initLogin } from './features/login/loginSlice'
5+
import { useAppDispatch } from './app/hooks'
46

5-
const App: React.FC = () => (
6-
<DataContainer>
7-
<Layout />
8-
</DataContainer>
9-
)
7+
const App: React.FC = () => {
8+
const dispatch = useAppDispatch()
9+
useEffect(() => {
10+
dispatch(initLogin())
11+
}, [dispatch])
12+
13+
return (
14+
<DataContainer>
15+
<Layout />
16+
</DataContainer>
17+
)
18+
}
1019

1120
export default App

src/app/hooks.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
2+
import type { RootState, AppDispatch } from './store'
3+
4+
// Use throughout your app instead of plain `useDispatch` and `useSelector`
5+
export const useAppDispatch = () => useDispatch<AppDispatch>()
6+
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

src/app/store.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'
2+
import loginReducer from '../features/login/loginSlice'
3+
4+
export const store = configureStore({
5+
reducer: {
6+
login: loginReducer,
7+
},
8+
})
9+
10+
export type AppDispatch = typeof store.dispatch
11+
export type RootState = ReturnType<typeof store.getState>
12+
export type AppThunk<ReturnType = void> = ThunkAction<
13+
ReturnType,
14+
RootState,
15+
unknown,
16+
Action<string>
17+
>

src/components/Layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import Login from './Login'
2+
import Login from '../features/login/Login'
33
import VisualizationContainer from './VisualizationContainer'
44
import styled from 'styled-components'
55
import { PeopleListContainer } from './PeopleList'

src/components/Login.tsx

Lines changed: 0 additions & 78 deletions
This file was deleted.

src/features/login/Login.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react'
2+
import LoginPrompt from './LoginPrompt'
3+
import { useAppDispatch, useAppSelector } from '../../app/hooks'
4+
import { login, logout, selectLoginStatus, selectSession } from './loginSlice'
5+
6+
interface Props {
7+
className?: string
8+
}
9+
10+
const Login: React.FC<Props> = (
11+
{ className, ...props }: Props = { className: '' },
12+
) => {
13+
const dispatch = useAppDispatch()
14+
const info = useAppSelector(selectSession)
15+
const status = useAppSelector(selectLoginStatus)
16+
17+
const commonProps = {
18+
...props,
19+
className: `${className} button`,
20+
}
21+
22+
return status === 'loading' ? (
23+
<button {...commonProps} disabled>
24+
Loading
25+
</button>
26+
) : info.isLoggedIn ? (
27+
<button {...commonProps} onClick={() => dispatch(logout())}>
28+
{info.webId} Logout
29+
</button>
30+
) : (
31+
<LoginPrompt
32+
{...commonProps}
33+
onLogin={oidcIssuer => dispatch(login(oidcIssuer))}
34+
/>
35+
)
36+
}
37+
38+
export default Login

src/features/login/loginAPI.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {
2+
login as solidLogin,
3+
logout as solidLogout,
4+
handleIncomingRedirect,
5+
} from '@inrupt/solid-client-authn-browser'
6+
7+
export const init = async () => {
8+
const sessionInfo = await handleIncomingRedirect({
9+
url: window.location.href,
10+
restorePreviousSession: true,
11+
})
12+
return sessionInfo
13+
}
14+
15+
export const login = async (oidcIssuer: string) => {
16+
try {
17+
await solidLogin({
18+
oidcIssuer,
19+
redirectUrl: window.location.href,
20+
clientName: 'Friends Crawler',
21+
})
22+
} catch (error) {
23+
alert(`Could not find a Solid Pod at ${oidcIssuer}`)
24+
localStorage.removeItem('idp')
25+
}
26+
}
27+
28+
export const logout = async () => {
29+
await solidLogout()
30+
}

0 commit comments

Comments
 (0)