Skip to content

Commit d5f109a

Browse files
fix(client): only fetch completion data on challenge pages (freeCodeCamp#55787)
1 parent 119bfdf commit d5f109a

File tree

7 files changed

+78
-80
lines changed

7 files changed

+78
-80
lines changed

client/src/components/Progress/progress.tsx

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
import React from 'react';
2-
import { connect } from 'react-redux';
1+
import React, { useEffect } from 'react';
2+
import { connect, ConnectedProps } from 'react-redux';
33
import { createSelector } from 'reselect';
44
import { TFunction } from 'i18next';
55
import { withTranslation } from 'react-i18next';
6+
import { useStaticQuery, graphql } from 'gatsby';
7+
68
import {
79
challengeMetaSelector,
810
currentBlockIdsSelector,
911
completedChallengesInBlockSelector,
1012
completedPercentageSelector
1113
} from '../../templates/Challenges/redux/selectors';
1214
import { liveCerts } from '../../../config/cert-and-project-map';
15+
import { updateAllChallengesInfo } from '../../redux/actions';
16+
import { CertificateNode, ChallengeNode } from '../../redux/prop-types';
1317
import ProgressInner from './progress-inner';
1418

1519
const mapStateToProps = createSelector(
@@ -40,9 +44,11 @@ const mapStateToProps = createSelector(
4044
})
4145
);
4246

43-
type StateProps = ReturnType<typeof mapStateToProps>;
47+
const mapDispatchToProps = { updateAllChallengesInfo };
48+
49+
type PropsFromRedux = ConnectedProps<typeof connector>;
4450

45-
interface ProgressProps extends StateProps {
51+
interface ProgressProps extends PropsFromRedux {
4652
t: TFunction;
4753
}
4854
function Progress({
@@ -52,14 +58,20 @@ function Progress({
5258
superBlock,
5359
completedChallengesInBlock,
5460
completedPercent,
55-
t
61+
t,
62+
updateAllChallengesInfo
5663
}: ProgressProps): JSX.Element {
5764
const blockTitle = t(`intro:${superBlock}.blocks.${block}.title`);
5865
// Always false for legacy full stack, since it has no projects.
5966
const isCertificationProject = liveCerts.some(cert =>
6067
cert.projects?.some((project: { id: string }) => project.id === id)
6168
);
6269

70+
const { challengeNodes, certificateNodes } = useGetAllBlockIds();
71+
useEffect(() => {
72+
updateAllChallengesInfo({ challengeNodes, certificateNodes });
73+
}, [challengeNodes, certificateNodes, updateAllChallengesInfo]);
74+
6375
const totalChallengesInBlock = currentBlockIds?.length ?? 0;
6476
const meta =
6577
isCertificationProject && totalChallengesInBlock > 0
@@ -84,6 +96,53 @@ function Progress({
8496
);
8597
}
8698

99+
// TODO: extract this hook and call it when needed (i.e. here, in the lower-jaw
100+
// and in completion-modal). Then we don't have to pass the data into redux.
101+
// This would mean that we have to memoize any complex calculations in the hook.
102+
// Otherwise, this will undo all the recent performance improvements.
103+
const useGetAllBlockIds = () => {
104+
const {
105+
allChallengeNode: { nodes: challengeNodes },
106+
allCertificateNode: { nodes: certificateNodes }
107+
}: {
108+
allChallengeNode: { nodes: ChallengeNode[] };
109+
allCertificateNode: { nodes: CertificateNode[] };
110+
} = useStaticQuery(graphql`
111+
query getBlockNode {
112+
allChallengeNode(
113+
sort: {
114+
fields: [
115+
challenge___superOrder
116+
challenge___order
117+
challenge___challengeOrder
118+
]
119+
}
120+
) {
121+
nodes {
122+
challenge {
123+
block
124+
id
125+
}
126+
}
127+
}
128+
allCertificateNode {
129+
nodes {
130+
challenge {
131+
certification
132+
tests {
133+
id
134+
}
135+
}
136+
}
137+
}
138+
}
139+
`);
140+
141+
return { challengeNodes, certificateNodes };
142+
};
143+
87144
Progress.displayName = 'Progress';
88145

89-
export default connect(mapStateToProps)(withTranslation()(Progress));
146+
const connector = connect(mapStateToProps, mapDispatchToProps);
147+
148+
export default connector(withTranslation()(Progress));

client/src/components/layouts/default.tsx

Lines changed: 4 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { useMediaQuery } from 'react-responsive';
55
import { connect } from 'react-redux';
66
import { bindActionCreators, Dispatch } from 'redux';
77
import { createSelector } from 'reselect';
8-
import { useStaticQuery, graphql } from 'gatsby';
98

109
import latoBoldURL from '../../../static/fonts/lato/Lato-Bold.woff';
1110
import latoLightURL from '../../../static/fonts/lato/Lato-Light.woff';
@@ -18,8 +17,7 @@ import { isBrowser } from '../../../utils';
1817
import {
1918
fetchUser,
2019
onlineStatusChange,
21-
serverStatusChange,
22-
updateAllChallengesInfo
20+
serverStatusChange
2321
} from '../../redux/actions';
2422
import {
2523
isSignedInSelector,
@@ -30,12 +28,7 @@ import {
3028
userFetchStateSelector
3129
} from '../../redux/selectors';
3230

33-
import {
34-
UserFetchState,
35-
User,
36-
AllChallengeNode,
37-
CertificateNode
38-
} from '../../redux/prop-types';
31+
import { UserFetchState, User } from '../../redux/prop-types';
3932
import BreadCrumb from '../../templates/Challenges/components/bread-crumb';
4033
import Flash from '../Flash';
4134
import { flashMessageSelector, removeFlashMessage } from '../Flash/redux';
@@ -95,8 +88,7 @@ const mapDispatchToProps = (dispatch: Dispatch) =>
9588
fetchUser,
9689
removeFlashMessage,
9790
onlineStatusChange,
98-
serverStatusChange,
99-
updateAllChallengesInfo
91+
serverStatusChange
10092
},
10193
dispatch
10294
);
@@ -138,8 +130,7 @@ function DefaultLayout({
138130
superBlock,
139131
theme,
140132
user,
141-
fetchUser,
142-
updateAllChallengesInfo
133+
fetchUser
143134
}: DefaultLayoutProps): JSX.Element {
144135
const { t } = useTranslation();
145136
const isMobileLayout = useMediaQuery({ maxWidth: MAX_MOBILE_WIDTH });
@@ -150,10 +141,8 @@ function DefaultLayout({
150141
const isExSmallViewportHeight = useMediaQuery({
151142
maxHeight: EX_SMALL_VIEWPORT_HEIGHT
152143
});
153-
const { challengeEdges, certificateNodes } = useGetAllBlockIds();
154144
useEffect(() => {
155145
// componentDidMount
156-
updateAllChallengesInfo({ challengeEdges, certificateNodes });
157146
if (!isSignedIn) {
158147
fetchUser();
159148
}
@@ -278,50 +267,6 @@ function DefaultLayout({
278267
}
279268
}
280269

281-
// TODO: get challenge nodes directly rather than wrapped in edges
282-
const useGetAllBlockIds = () => {
283-
const {
284-
allChallengeNode: { edges: challengeEdges },
285-
allCertificateNode: { nodes: certificateNodes }
286-
}: {
287-
allChallengeNode: AllChallengeNode;
288-
allCertificateNode: { nodes: CertificateNode[] };
289-
} = useStaticQuery(graphql`
290-
query getBlockNode {
291-
allChallengeNode(
292-
sort: {
293-
fields: [
294-
challenge___superOrder
295-
challenge___order
296-
challenge___challengeOrder
297-
]
298-
}
299-
) {
300-
edges {
301-
node {
302-
challenge {
303-
block
304-
id
305-
}
306-
}
307-
}
308-
}
309-
allCertificateNode {
310-
nodes {
311-
challenge {
312-
certification
313-
tests {
314-
id
315-
}
316-
}
317-
}
318-
}
319-
}
320-
`);
321-
322-
return { challengeEdges, certificateNodes };
323-
};
324-
325270
DefaultLayout.displayName = 'DefaultLayout';
326271

327272
export default connect(

client/src/redux/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const initialState = {
6666
...defaultFetchState
6767
},
6868
allChallengesInfo: {
69-
challengeEdges: [],
69+
challengeNodes: [],
7070
certificateNodes: []
7171
},
7272
userProfileFetchState: {

client/src/redux/prop-types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export type CertificateNode = {
243243
};
244244

245245
export type AllChallengesInfo = {
246-
challengeEdges: { node: ChallengeNode }[];
246+
challengeNodes: ChallengeNode[];
247247
certificateNodes: CertificateNode[];
248248
};
249249

client/src/templates/Challenges/components/completion-modal.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@ import { createSelector } from 'reselect';
88
import { Button, Modal } from '@freecodecamp/ui';
99

1010
import Login from '../../../components/Header/components/login';
11-
import {
12-
isSignedInSelector,
13-
allChallengesInfoSelector
14-
} from '../../../redux/selectors';
15-
import { AllChallengesInfo, ChallengeFiles } from '../../../redux/prop-types';
11+
import { isSignedInSelector } from '../../../redux/selectors';
12+
import { ChallengeFiles } from '../../../redux/prop-types';
1613
import { closeModal, submitChallenge } from '../redux/actions';
1714
import {
1815
completedChallengesIdsSelector,
@@ -35,7 +32,6 @@ const mapStateToProps = createSelector(
3532
completedChallengesIdsSelector,
3633
isCompletionModalOpenSelector,
3734
isSignedInSelector,
38-
allChallengesInfoSelector,
3935
successMessageSelector,
4036
isSubmittingSelector,
4137
(
@@ -44,7 +40,6 @@ const mapStateToProps = createSelector(
4440
completedChallengesIds: string[],
4541
isOpen: boolean,
4642
isSignedIn: boolean,
47-
allChallengesInfo: AllChallengesInfo,
4843
message: string,
4944
isSubmitting: boolean
5045
) => ({
@@ -54,7 +49,6 @@ const mapStateToProps = createSelector(
5449
completedChallengesIds,
5550
isOpen,
5651
isSignedIn,
57-
allChallengesInfo,
5852
message,
5953
isSubmitting
6054
})

client/src/templates/Challenges/redux/completion-epic.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ describe('completionEpic', () => {
1919
challenge: { challengeMeta: { challengeType: 0 } },
2020
app: {
2121
user: { username: 'test' },
22-
allChallengesInfo: { challengeEdges: [], certificateNodes: [] }
22+
allChallengesInfo: { challengeNodes: [], certificateNodes: [] }
2323
}
2424
}
2525
};

client/src/utils/get-completion-percentage.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ export const getCurrentBlockIds = (
3939
certification: string,
4040
challengeType: number
4141
): string[] => {
42-
const { challengeEdges, certificateNodes } = allChallengesInfo;
42+
const { challengeNodes, certificateNodes } = allChallengesInfo;
4343
const currentCertificateIds =
4444
certificateNodes
4545
.filter(node => node.challenge.certification === certification)[0]
4646
?.challenge.tests.map(test => test.id) ?? [];
47-
const currentBlockIds = challengeEdges
48-
.filter(edge => edge.node.challenge.block === block)
49-
.map(edge => edge.node.challenge.id);
47+
const currentBlockIds = challengeNodes
48+
.filter(node => node.challenge.block === block)
49+
.map(node => node.challenge.id);
5050

5151
return isProjectBased(challengeType)
5252
? currentCertificateIds

0 commit comments

Comments
 (0)