Refractoring
This is going to be a long and painful session to read through. It won't have a lot of structure, what we will do is take a bunch of repeated code, and abstract it so we don't have to see it in our face all the time.
So, here comes just a bunch of chapters without any specific order.
Error Handling in back/db.js
back/db.jsCurrently, our db.js file has this structure:
method1
try
call SQL
catch
send error message
method2
try
call SQL
catch
send error message
method3
try
call SQL
catch
send error message
method4
...etcHow about, instead, we do:
Let's look at a those few methods and study what is the common part between them.
It seems the common part is as follows:
The unique code parts, we can't pass them to the function. However, we can pass the rest of the things.
Our function would, then, take three arguments, and return a result
Another part we can extract is this:
Let's make a generic function that checks we have the proper parameters. The function itself will be more complex, but when used, it will be clearer. Here's the function:
In the example above, the function will throw
property "c" is necessary,If no errors are thrown, then the object, untouched, is returned
@param {Object} obj an object to check
@param {Array} names an array of property names
@returns {Object} the object itself
*/
const ensurePropertiesExist = (obj, names) => {
if(!obj){
throw new Error(
the properties+names.join(',')+' are necessary')}
for(let i=0; i < names.length; i++){
const propertyName = names[i]
if(obj[propertyName] === null || typeof obj[propertyName] === 'undefined' ){
throw new Error(
property "${propertyName}" is necessary)}
}
return obj
}
```
If we use those two functions, we can change createContact to the below:
Compare with the previous method. None of the functionality of createContact has changed, however, it is much easier to read. Each line expressed its intent in a much clearer manner than before.
Here are the rest of the methods:
There are few notable changes:
previously, we called the database queries sometimes
query, and sometimesstatement. We consolidated that. Everything we send is aquery.statementis what gets returned fromdb.run.Similarly, we've replaced variables called
idbycontact_id, which is clearer.previously, when a user failed to get deleted, an error was thrown. Now, we just return
false. This is a change in functionality, not just style, but it's worth the consistency.Most importantly, we had methods with two arguments, and methods with one. For example, why is
updateContactusing two arguments (contact_idandprops) instead of justpropslike every other? There is no good reason, and so, we made all methods use one argument, props. Of course, this means we need to change how we call the methods in our controller. We'll do that below.
As a result, our code is much easier to read and to parse.
Cleaning up back/src/index.js
back/src/index.jsLet's do the same thing we were doing before. Let's observe most methods. They can all be summed to:
This translates to the following function:
We'll use it like so:
Again, we cleaned up without changing any functionality.
We'll also move the uploading code in a separate file so we don't have to read it everytime we open index.js. Create a new file, back/src/upload.js:
In index.js:
Using Axios
We've concocted our own helpers for fetch, but actually, there is much better to do. We can use Axios, which already does all the things we've written functions for.
Let's install it. Move to the front, and do:
Import it:
Then we could simply exchange fetch('/blah') to axios.get('/blah') and fetch('/blah',{method:'POST'}) to axios.post('/blah'), but we'll just do a little better. We don't need the makeRequestURL method, because Axios already allows us to pass parameters as an object.
We know the common part of all the request functions:
We'll replace makeURL, makeRequestURL, and ObjectToQuery. Remove them from App.js and utils.js
Instead, in App.js, inside the App class, write the following function:
This function will do the request for us, set all the parameters for us. It's missing one thing though; we need to handle uploads. Here's a generic algorithm that takes care of handling POST parameters for us:
Let's add this to our previous function. Here's the complete one:
We can now use it. Here's for example getContactsList:
As you see, we have one relatively long function, but other methods become much smaller. We've eliminated a lot of repetition.
Here are the other methods:
Remove Cruft
Now is a very good time to comb you app. Look at the App's state. Read every file, make sure your methods are named correctly and make sense. Make sure there aren't unused variables.
Fix all the warnings (and, of course, the errors) that show in your console. Get your app real clean, because this is where it is still small enough to be read in an hour. You're at proof of concept stage; it's a viable, working application. It's imperative to continuously manage complexity before advancing forward.
This is a good time for testing, noting everything that is wrong, and planning on how to fix those things.
Optional: Moving Render Functions to their Pages
In the file front/src/App.js, there are a bunch of renderXXX functions, that take a lot of space. We're going to move all of these each in their own component.
Those methods are:
renderUserrenderUserLoggedOutrenderUserLoggedInrenderHomePagerenderContactPagerenderProfilePagerenderCreateFormPagerenderContent
Out of these, renderContent really has its place inside App; it doesn't make much sense to abstract it.
Everything else could move.
note: everything that was available in the scope, such as this.submit, will have to be passed as a prop.
For example
will become
And the function will just use the component
note 2: This is a lot of transferring props forward. It adds a lot of indirection for very little gain.
IT IS ENTIRELY VALID TO NOT DO THAT
You can just leave everything in App.js, and put things in external components the first time we reuse one.
Last updated
Was this helpful?