Git Code Commit (all commits between last post and this commit (link))
Today, I am going to pause before starting the Cascading Style Sheets (CSS) The Globe In My Bucket List Application (TGIMBA) blog post series. I want to revisit some the of the code that has been laid down. Part of any development process is always to refactor since it is (at least for me) never perfect the first time 🙂
So, first on the refactor list is React Redux JS. I am really intrigued with the technology since it is so different from what I have done in the past. In hind sight, I have never liked having to use window.location for navigation between components. It seems like overkill. So, researching revealed the ‘withRouter’ part of ‘react-router-dom’ (see reference #1 for more details). Basic usage is to import:
- import withRouter from ‘react-router-dom’;
And then use someone on the component X.js file:
Used in the Add.js component:
There seem to be some caveats to its use and seems limited in scope it only works in the component x.js file. I was not able to get it to work in the store component.
So, given this and the fact that Main.js is not behaving as I think it should (or more appropriately, I am not fully understanding the architecture pattern), I created a hello world React Redux JS application to explore its basic work flow functionality (again).
What did I learn? State is one of the most important things in React Redux JS and it should only exist in the reducer store. The ‘chain’ seems to be:
- You start off with the initial state as an object with null/empty/etc. values in your store.
- An action is called and some data is retrieved from the server and it is dispatched to the reducer.
- The reducer (depending on the type) returns a modified state.
- The component receives its data from props.
- View before data is loaded.
View after data is loaded.
If you noticed the text box, you may be wondering why I left this in. Well, the above statement breaks down with <input /> tags. This (among other Hyper Text Markup Language (HTML) controls) changes mostly on user interaction. So, it seems like there is an exception to the ‘state only exists in store’ rule. More specifically, in the case of HTML controls requiring user interaction, state must exist inside the component. Reference #5 is an excellent discussion on this. But for my purposes, I have accepted that state needs to exist in both places, but for different reasons.
In the case of the component, you create your initial user input required state in the constructor, then take it from state (again) on each render. The state is updated using the setState(args) method on the onChange event. This is the only acceptable time state should be used/updated in a component x.js file (from what I have seen so far).
So, this.props.history.push(‘/x’) works for the components. But where did that leave my original problem of navigation calls from the redux store? Initially, I tried passing the history object back to the reducer and this worked some of the time. But I abandoned it because it seemed clunky and I would get intermittent errors.
All excited from the hello world experiment and being able to pass the new state back and set the property, I set out to update TGIMBA. First was Add.js. Long story short, it didn’t work. I think this has to do because I am not using the BucketListItem.js reducer and using the Add.js reducer (BucketListItem.js is a sub-component of Add.js). If you remember, I wanted to share a component between Add.js and Edit.js. I tried multiple flavors, but not matter what I did, I was not able to fire code (i.e. a test in render(), using componentDidUpdate/componentWillReceiveProps/etc.) that redirected to /main. I was able to get it to work when I moved the action call to component/Add.js. But, I don’t want to lose the shared component. There probably is a way to do this, but I am moving on for now. I did move the server call from the reducer to the action.
Another issue that I still can’t explain. The Visual Studio 2017 template generated React Redux JS project had a couple examples of HTTP GET that had the code inside the action…not the reducer. I had initially stuck it in the reducer because that seem to make sense. In this refactor effort, I moved all of these calls out of the reducer and into the action. It looks like this in the store/Login.js.
However, after running the selenium tests to verify functionality, I noticed that the completed true/false value was always false. Tracing it down, the completed variable was coming in as undefined in the action. More specifically, it was being called twice – first with completed set, and then again as undefined. I read a lot of posts and tried a number of ways to fix this. However, I was never able to figure out why. But, through a lot of trial and error, it started working again when I moved the HTTP call back to the reducer for store/Add.js and store/Edit.js. It must be because I am using a child component to pass the values up to the parent and then to that component’s store.
Whatever the reason, this works:
In conclusion, I got some of the refactor items done, but not all. The HTTP calls still exist in some of the reducers. But, I am moving on for now.
Next will be the Cascading Style Sheets (CSS) blog series where will will make this functional site beautiful.
- Stock React Redux JS application generated from Visual Studio 2017 template