Unverified Commit 7a8f2b61 authored by Paul Kaplan's avatar Paul Kaplan Committed by GitHub

Merge pull request #5231 from paulkaplan/use-getstate-in-thunks

Use getState in thunks instead of passing props around
parents 71bb3a76 bd279fe5
......@@ -186,3 +186,6 @@ module.exports.setMoreCommentsToLoad = moreCommentsToLoad => ({
module.exports.resetComments = () => ({
type: 'RESET_COMMENTS'
});
// Selectors
module.exports.selectCommentCount = state => state.comments.comments.length;
......@@ -18,9 +18,19 @@ const {
setError,
setReplies,
setRepliesDeleted,
setRepliesRestored
setRepliesRestored,
selectCommentCount
} = require('../redux/comments.js');
const {
selectIsAdmin,
selectToken
} = require('./session');
const {
selectStudioId
} = require('./studio');
const getReplies = (studioId, commentIds, offset, isAdmin, token) => (dispatch => {
dispatch(setFetchStatus('replies', Status.FETCHING));
const fetchedReplies = {};
......@@ -50,8 +60,13 @@ const getReplies = (studioId, commentIds, offset, isAdmin, token) => (dispatch =
});
});
const getTopLevelComments = (id, offset, isAdmin, token) => (dispatch => {
const getTopLevelComments = () => ((dispatch, getState) => {
dispatch(setFetchStatus('comments', Status.FETCHING));
const state = getState();
const id = selectStudioId(state);
const offset = selectCommentCount(state);
const isAdmin = selectIsAdmin(state);
const token = selectToken(state);
api({
uri: `${isAdmin ? '/admin' : ''}/studios/${id}/comments`,
authentication: token ? token : null,
......
......@@ -3,7 +3,7 @@ const keyMirror = require('keymirror');
const api = require('../lib/api');
const log = require('../lib/log');
const {selectUserId, selectIsAdmin, selectIsSocial} = require('./session');
const {selectUserId, selectIsAdmin, selectIsSocial, selectUsername, selectToken} = require('./session');
const Status = keyMirror({
FETCHED: null,
......@@ -77,10 +77,31 @@ const setRoles = roles => ({
roles: roles
});
// Thunks
// Selectors
// Fine-grain selector helpers - not exported, use the higher level selectors below
const isCreator = state => selectUserId(state) === state.studio.owner;
const isCurator = state => state.studio.curator;
const isManager = state => state.studio.manager || isCreator(state);
// Action-based permissions selectors
const selectCanEditInfo = state => selectIsAdmin(state) || isManager(state);
const selectCanAddProjects = state =>
isManager(state) ||
isCurator(state) ||
(selectIsSocial(state) && state.studio.openToAll);
// This isn't "canComment" since they could be muted, but comment composer handles that
const selectShowCommentComposer = state => selectIsSocial(state);
// Data selectors
const selectStudioId = state => state.studio.id;
const getInfo = studioId => (dispatch => {
// Thunks
const getInfo = () => ((dispatch, getState) => {
dispatch(setFetchStatus('infoStatus', Status.FETCHING));
const studioId = selectStudioId(getState());
api({uri: `/studios/${studioId}`}, (err, body, res) => {
if (err || typeof body === 'undefined' || res.statusCode !== 200) {
dispatch(setFetchStatus('infoStatus', Status.ERROR, err));
......@@ -99,8 +120,12 @@ const getInfo = studioId => (dispatch => {
});
});
const getRoles = (studioId, username, token) => (dispatch => {
const getRoles = () => ((dispatch, getState) => {
dispatch(setFetchStatus('rolesStatus', Status.FETCHING));
const state = getState();
const studioId = selectStudioId(state);
const username = selectUsername(state);
const token = selectToken(state);
api({
uri: `/studios/${studioId}/users/${username}`,
authentication: token
......@@ -119,23 +144,6 @@ const getRoles = (studioId, username, token) => (dispatch => {
});
});
// Selectors
// Fine-grain selector helpers - not exported, use the higher level selectors below
const isCreator = state => selectUserId(state) === state.studio.owner;
const isCurator = state => state.studio.curator;
const isManager = state => state.studio.manager || isCreator(state);
// Action-based permissions selectors
const selectCanEditInfo = state => selectIsAdmin(state) || isManager(state);
const selectCanAddProjects = state =>
isManager(state) ||
isCurator(state) ||
(selectIsSocial(state) && state.studio.openToAll);
// This isn't "canComment" since they could be muted, but comment composer handles that
const selectShowCommentComposer = state => selectIsSocial(state);
module.exports = {
getInitialState,
studioReducer,
......@@ -146,6 +154,7 @@ module.exports = {
getRoles,
// Selectors
selectStudioId,
selectCanEditInfo,
selectCanAddProjects,
selectShowCommentComposer
......
import React, {useEffect, useCallback} from 'react';
import React, {useEffect} from 'react';
import PropTypes from 'prop-types';
import {useParams} from 'react-router-dom';
import {connect} from 'react-redux';
......@@ -13,7 +13,7 @@ import {selectShowCommentComposer} from '../../redux/studio.js';
const StudioComments = ({
comments,
getTopLevelComments,
handleLoadMoreComments,
handleNewComment,
moreCommentsToLoad,
replies,
......@@ -21,13 +21,9 @@ const StudioComments = ({
}) => {
const {studioId} = useParams();
const handleLoadComments = useCallback(() => {
getTopLevelComments(studioId, comments.length);
}, [studioId, comments.length]);
useEffect(() => {
if (comments.length === 0) getTopLevelComments(studioId, 0);
}, [studioId]);
if (comments.length === 0) handleLoadMoreComments();
}, []); // Only runs once after the first render
return (
<div>
......@@ -58,7 +54,7 @@ const StudioComments = ({
{moreCommentsToLoad &&
<Button
className="button load-more-button"
onClick={handleLoadComments}
onClick={handleLoadMoreComments}
>
<FormattedMessage id="general.loadMore" />
</Button>
......@@ -70,7 +66,7 @@ const StudioComments = ({
StudioComments.propTypes = {
comments: PropTypes.arrayOf(PropTypes.shape({})),
getTopLevelComments: PropTypes.func,
handleLoadMoreComments: PropTypes.func,
handleNewComment: PropTypes.func,
moreCommentsToLoad: PropTypes.bool,
replies: PropTypes.shape({}),
......@@ -86,7 +82,7 @@ export default connect(
shouldShowCommentComposer: selectShowCommentComposer(state)
}),
{
getTopLevelComments: studioCommentActions.getTopLevelComments,
handleLoadMoreComments: studioCommentActions.getTopLevelComments,
handleNewComment: studioCommentActions.addNewComment
}
)(StudioComments);
import React, {useEffect} from 'react';
import PropTypes from 'prop-types';
import {useParams} from 'react-router-dom';
import {connect} from 'react-redux';
import Debug from './debug.jsx';
import {selectUsername, selectToken} from '../../redux/session';
import {selectIsLoggedIn} from '../../redux/session';
import {getInfo, getRoles, selectCanEditInfo} from '../../redux/studio';
const StudioInfo = ({username, studio, token, canEditInfo, onLoadInfo, onLoadRoles}) => {
const {studioId} = useParams();
const StudioInfo = ({isLoggedIn, studio, canEditInfo, onLoadInfo, onLoadRoles}) => {
useEffect(() => { // Load studio info after first render
if (studioId) onLoadInfo(studioId);
}, [studioId]);
onLoadInfo();
}, []);
useEffect(() => { // Load roles info once the username is available
if (studioId && username && token) onLoadRoles(studioId, username, token);
}, [studioId, username, token]);
useEffect(() => { // Load roles info once the user is logged in is available
if (isLoggedIn) onLoadRoles();
}, [isLoggedIn]);
return (
<div>
......@@ -35,8 +32,7 @@ const StudioInfo = ({username, studio, token, canEditInfo, onLoadInfo, onLoadRol
StudioInfo.propTypes = {
canEditInfo: PropTypes.bool,
username: PropTypes.string,
token: PropTypes.string,
isLoggedIn: PropTypes.bool,
studio: PropTypes.shape({
// Fill this in as the data is used, just for demo now
}),
......@@ -47,13 +43,11 @@ StudioInfo.propTypes = {
export default connect(
state => ({
studio: state.studio,
username: selectUsername(state),
token: selectToken(state),
isLoggedIn: selectIsLoggedIn(state),
canEditInfo: selectCanEditInfo(state)
}),
dispatch => ({
onLoadInfo: studioId => dispatch(getInfo(studioId)),
onLoadRoles: (studioId, username, token) => dispatch(
getRoles(studioId, username, token))
})
{
onLoadInfo: getInfo,
onLoadRoles: getRoles
}
)(StudioInfo);
......@@ -78,5 +78,13 @@ render(
[activity.key]: activity.reducer,
studio: studioReducer,
comments: commentsReducer
},
{
studio: {
// Include the studio id in the initial state to allow us
// to stop passing around the studio id in components
// when it is only needed for data fetching, not for rendering.
id: window.location.pathname.split('/')[2]
}
}
);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment