It’s that time again: Portfolio Project Time, React/Redux Edition!
Let’s touch on what React is real quick: React is a nifty framework, and is how the cool kids have been making their web apps these days. For mobile apps, there’s React Native, which allows a person to write their code in ReactJS, while letting it render in the “native platform UI”, meaning that it essentially allows the coder to do the job of writing the app and let the framework worry about it working in Android or iOS. Why am including this bit? Because when you’re Googling for an answer to an issue you have, make sure you aren’t reading blogs or StackOverflow links about React Native.
There’s a little preamble that every blog post gives when the topic is something to do with ReactJS. Namely, it always takes up some space telling you how to create a new app. Well, I’m going to skip that for now, as you can find that in about a dozen other blogs just by searching for “react” + literally any other issue.
So what exactly is this topic about? I’m going to talk through some more coding anxiety. My last blog post discussed how with Javascript, nothing could be working and you can get so little guidance as to what you’re doing wrong. I’ve been meaning to post a follow-up to that with some more quick and dirty testing methods, but that’s going to happen after this one. This post is going to be about some aggravations I would run into and how I got around them. So this post and the rest of its brethren are not going to be about elegant solutions and beautiful code, but I’m also not going to glorify the brute force or less than ideal solutions. Rather, sometimes, you just need something to work, to tick off the boxes and move on. The important thing is, just accept that it’s not done yet. Success in coding is built on failure, about seeing a different error message, or getting the code far enough that you’re not as embarassed to show it to somebody. I get it. That’s the area I live in. My projects keep coming in later and later, and I wish it was because I was making bigger and grander and more spectacular projects, but in the end, it’s just cause I’m getting more frustrated, more anxious, and because of the last two, more tired. But! I end up passing, maybe doing some refactoring, and it’s on to the next one! If you’re in a program, there’s people to talk to, a wealth of information to read, and plenty of people out there who are very excited to get you to the next step without calling you names. It’s gonna be ok, really.
Now let’s get on to some useful things that may save you some time, or at least a little stress, when you’re working on a React/Redux project.
Where did my props go?
You did it right, you’ve got react-redux
, redux
, react
, and react's-mother
all in your file. You’ve imported connect
, got your store built, and got your mapDispatchToProps
function built, or you’ve shorthanded those action-creators in the args of your export default connect(null, mapDispatchToProps)(Component)
little ditty at the bottom of your file. So, uh why am I getting that is not a function
or undefined
errors for my props?
I’m going to assume you:
1) added the store, either to an index of stores whereupon you’ve combined your many reducers, or simply passed the one store in directly at some point
2) properly imported your action creators and that your action creators link up properly to your store. (Big one, make sure your action in your action creator and the action in the redux store are spelled the same. Computers aren’t smart enough to tell the difference between {type: "ADD_POSTS"}
and {type: "ADD_POST"}
) OR that your mapDispatchToProps
function was written properly.
The first place I’m going to look is that line where you call connect()
. If you aren’t doing a mapStateToProps
function, it’s very important that you pass null
in first. This is a very easy thing to overlook, as a lot of things you read about Redux have a mapStateToProps
as a first arg. Here’s the important thing to remember: they’re arguments, yes, but remember how you write a function. The function doesn’t care what you call the arguments, just what order they’re in. So if it’s expecting a string, then an integer, you’re going to have issues when you pass in 3
, then banana
. Similarly, connect()
doesn’t care that the args being passed in are called mapStateToProps
and mapDispatchToProps
. This is just a convention so that other people can read your code. You could name those functions (note: I know, they’re written as consts ) apple
and banana
and they would work exactly the same. (Will they not? Tell me in the comments. Also, don’t do that). This can be confusing to people who interact with languages like Ruby, specifically Rails, where the built-in tools look for specifically named files and methods to work, unless you go out of your way to specify for them to do otherwise.
Redux doesn’t quite work that way. It’s almost more of a mental model, and it relies heavily on you to maintain it. This also lends a degree of flexibility to your project. Does this component have to actually rely on the Redux store? Can I make a prop on a different level and then pass it down to keep the state out of my components? Sure you can!
So here’s another little bit. My project app was to do three fetch()
requests to populate my store with three arrays of differing length, which I would then shuffle, and pull one item from each. So, if my example above seemed a little specific, well, I struggled with that little issue a little longer than I’m happy to admit. How did I fix it? I pulled the componentDidMount()
lifecycle method up one level, did a mapStateToProps
at that component, and then passed those props down to the child component. This can be a very important problem-solving method in React: trying things somewhere else. It could be a solution you end up working your way around, or it could just be another headscratcher, but it might just confirm that at least your code works.
Don’t get ahead of yourself
Generally, when writing Redux, you’re supposed to start a new component at the store (at least, when it’s connected to the store. Remember, it doesn’t always have to be). Except people don’t seem to do this when you’re watching code-a-long, “let’s-build-a” type videos. They almost always seem to build them front to back, starting with the component, and then hooking that component up to the store. So don’t get down on yourself if you can’t think that way and have to look at the rendered page first and then build the store. Just be sure to do one thing at a time. I thought that because what I was building was somewhat simple, I could build a lot of it out very quickly, then go back and build the “back-end” (but not the API!) code later. This was a mistake. The basic idea of the app involved a post and comment system as a way of building narratives. Each post would have an input box for comments right below; the posts themselves were laid out in a flex grid along the bottom of the window. The structure, therefore, would go PostContainer -> Posts -> Post -> Comments Container -> Comments -> Comment
, leaving out branches for PostInput
and CommentInput
. This structure, with placeholders to show their presence (“This is a post!”, “This is a comment!” etc), rendered out just fine. However, the CommentInput
bit of code seemed cause a dreaded white crash. A blank page was rendered, with no error messages in my server displays, no indication as to what it didn’t like about that component. I commented out bit by bit, and it turned out it the presence of text input that it didn’t like. Whether it was <input type='text'>
or <textarea>
, the app wouldn’t render. If I got rid of that element and only that element, the comments rendered fine. :shrug: What can you do? Does React not like nesting that far? It’s hard to tell, worse still, because that effectively cut off a significant portion of my project’s functionality. Because I had spent a bunch of time writing out that skeleton code before filling it out with any sort of data handling capacity, I’d really cut myself from a lot of time I could have spent rewriting and fixing the code. In retrospect, comments should only be visible in an individual posts “Show” page, so to speak, which would also have cut off issues with comments from dooming the rest of the app.
Test, and check, and test.
This is pretty simple. Log your props and state constantly. Debugger mode constantly. Whenever you write a button, the first thing it should do is log what’s being passed in to the callback function out to the console. Make sure that the objects that are constantly being thrown around your app are being named clearly and conventionally, and make sure they’re not being double nested (this.props.posts.posts
appears quite a few times). Similarly, just before the return()
of your class or function that makes up a component, throw a log there to see what’s rendering and how. If you’re getting nulls or undefined, it’s all good, at least you now know where and when it’s happening.
Oh, and if your component is export default
? Yeah, don’t put curly brackets around its name when you’re importing it. Connect()
doesn’t like that.
Want some screenshots, better explanations of technical elements, or …anything? Let me know, and I’ll make it so!