I'm currently working on a project with React and Firebase and at the moment my logic to write new data to Firestore is directly in my components. I'm now also integrating Redux, for this I am using react-redux-firebase. My question is now, where is the best place to put this logic, because it makes my component quiet bloated.
In the documentation of react-redux-firebase, they've also put it directly in the component, but their logic is quit simple, mine is a little bit more complex, like this handleSubmit:
const handleSubmit = async (e) => {
e.preventDefault();
setDisabled(true); //disable button to prevent multiple sbumits
setLoading(true);
try {
const docRef = await firestore.collection("goodies").add(newGoodieData); // create new event
await firestore.collection("goodies").doc(docRef.id).update({
// add eventId to event doc
id: docRef.id,
});
if (image) {
const imageName = docRef.id + "." + image.name.split(".").pop(); //create image name with eventID and file extions
const imageRef = storage.ref().child("goodie_images/").child(imageName); //create imageRef
const imageSnapshot = await imageRef.put(image); //upload image to storage
const downloadUrl = await imageSnapshot.ref.getDownloadURL(); //get image download-url
const imagePath = imageRef.fullPath; // get image storage path
await firestore.collection("goodies").doc(docRef.id).update({
//update event with imageUrl and imagePath
image: downloadUrl,
imagePath: imagePath,
});
}
setDisabled(false);
setLoading(false);
dispatch(showSuccessToast(`Created new goodie "${newGoodieData.name}"!`));
history.push("/goodies");
} catch (error) {
dispatch(showErrorToast("Ops, an error occurred!"));
console.error(error.message);
}
};
This is entirely my idiosyncrasy, but I use a structure like the below. In general, component and page (i.e react component) names are initial-capital:
src
|-blocks
|-components
|-css
|-navigation
|-pages
|-services
|-App.js
|-constants.js
|-index.js
BLOCKS
These are my application-specific components, and some larger application-specific functions.
|-blocks
|-Block1
| |-index.js
| |-aSubModule.js
|-Block2
| |-index.js
| |-aSubModule.js
etc, etc
COMPONENTS
These are "generalized" components - i.e could be released as modules, or are customized from external modules. These are NOT application-specific. Similar structure to "blocks"
|-components
|-ComponentThatDoesThisThing
|-ComponentThatDoesThatThing
etc, etc
CSS
As the name says...
NAVIGATION
Headers, Footers, navigation menus. Also navigation helper functions (such as wrapping links to pages, etc)
|-navigation
|-Header
|-Menu
|-Footer
etc, etc
PAGES
All my react pages and sub-pages, to whatever depth are needed. Everything below pages is specific to the application
|-pages
|-Users
| |-Home
| | |index.js
| | |-etc,etc
| |-Account
| |-Bio
| |-etc,etc
|-Owners
| |-Home
| |-Account
| |-Bio
| |-etc,etc
|-index.js
And where your question is more directly addressed:
SERVICES
|-services
|-reduxEnhancers
|-reduxMiddleware
|-reduxRootReducers
|-slices
|-configureStore.js
|-firestore.js
Not yet much different than the tutorials. But this is what I have in my slices - which more-or-less-ish correspond to redux state slices:
SLICES
|-services
|-slices
|-UserSlice
| |-actions
| | |-index.js
| |-reducer
| | |-index.js
| |-business
| | |-index.js
| |-index.js
|-OwnerSlice
| |-actions
| | |-index.js
| |-reducer
| | |-index.js
| |-business
| | |-index.js
| |-index.js
|-KitchCupboardSlice
| |-actions
| | |-index.js
| |-reducer
| | |-index.js
| |-business
| | |-index.js
| |-index.js
|-etc,etc slices
|-index.js
IMPORTANT NOTE: most Redux tutorials show the Redux slices having the same branching structure as the React components. In general THIS IS NOT AT ALL THE CASE, and I have yet to find a case where it is even helpful. The tree-like structure of your data is not necessarily at all like the tree-like structure of your pages and react components - divide your Redux store the way the data wants you to, not the way the user-facing React wants you to. Even more to the point for this forum: let the FIRESTORE structure of your data guide you.
In use:
|-services
|-slices
|-UserSlice
|-index.js
...for example, imports and then exports all the public/exported symbols from actions
, reducer
and business
.
|-services
|-slices
|-UserSlice
|-reducer
...reducer
uses store.injectReducer to add itself to the Redux store, using symbols and constants defined in
|-services
|-slices
|-UserSlice
|-actions
...actions
, which also creates the specific Redux actions and helper boiler plate (actions, selectors, etc).
To help separate responsibility:
|-services
|-slices
|-UserSlice
|-business
...business
sub-directory holds the BUSINESS logic you mentioned - any logic that a React component might need to call that is NOT specifically local to the display & state aspects of the React modules. This is ALSO where I define any access to firestore, or any other external service or fetches - the React components should NOT care how this is done.
To make accessing all of these various exports more convenient, I use
|-services
|-slices
|-index
...which imports and then exports all the public/exported symbols from all the slices. Makes it easier to import into various React comopnents without worrying about changes to the underlying structure of slices
As I said, COMPLETELY my idiosyncrasy, but it helps to separate out responsibility.