Refactoring a React app to use Redux and Thunk: Part One, the React App
Getting started with React is fairly trivial for someone who already has a bit of Javascript under their belt. There are tons of online courses that will get you started — one I worked with was a Pluralsight (paid) course React JS Getting Started. There are plenty of others.
So I’m going to walk through the React app I built, but the meat of this is going to handle the refactor from React to Redux. I decided to break this out to its own article because I intend to refactor a couple more times after this.
What we’re building
After finishing the Getting Started With Redux course (also Pluralsight) with Dan Abramov, I had some Redux thoughts floating in my head, but wanted to apply them to a real coding problem. Dan builds a to-do app and if I try to build one of those, apparently I will be responsible for canine murder (plus, boring). A little googling and I hit upon the NASA open data API. Now this we can use. Why not use the NASA search API (bonus, doesn’t require API keys) to make some async requests and then save some stories to read later? If we’re honest, it’s basically a to-do app but at least we get some nice pictures.
Our incredibly simple app has only two features:
- Search NASA for space-related news stories
- Save (and delete) stories for future reading
I’m no designer, so forgive the super-basicness of this app. Here’s what the final thing looks like:

The React app
Our initial application is so simple it could be built inside a single file. But we break things into components so we can keep our code DRY. Here’s App.js. I’m assuming you’re familiar with tooling like create-react-app and I don’t need to get into all the details of the rest of the stuff (including styling). The final code-base is on Github.
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.css';
import { Input, Button } from 'reactstrap';
import './App.css';import ResultsList from './components/ResultsList';
import NasaQuery from './components/NasaQuery';class App extends Component {
state = {
query: '',
results: [],
noResults: '',
saved: [],
page: 'home'
}render() {
const handleChange = e => {
e.preventDefault();
this.setState({
query: e.target.value
})
}const handleSubmit = e => {
e.preventDefault();
requestFromNASA();
}const requestFromNASA = () => {
fetch(`https://images-api.nasa.gov/search?q=${this.state.query}`)
.then( resp => resp.json())
.then ( resp => {
if (resp.collection.items.length > 0) {
this.setState({
results: resp.collection.items.slice(0, 20),
noResults: ''
})
} else {
this.setState({
noResults: `Your search for ${this.state.query} returned no results`
})
}
})
}const handleSave = (item) => {
this.setState( state => {
return {
saved: state.saved.concat(item)
}
})
}const toggleSavedView = (e) => {
e.preventDefault();
if (this.state.page === 'home') {
this.setState({
page: 'saved'
})
} else {
this.setState({
page: 'home'
})
}
}return (
<div className="App">
<div className="App-header">
<h2>Nasa Search</h2>
{ this.state.page === 'home' &&
<a href='/saved' className='orange-link'
onClick={toggleSavedView}>View Saved</a> }
{ this.state.page === 'saved' &&
<a href='/home' className='orange-link'
onClick={toggleSavedView}>Home</a> }
</div>
{this.state.page === 'home' &&
<div>
<NasaQuery query={this.state.query}
handleSubmit={handleSubmit}
handleChange={handleChange}/>
<ResultsList saved={this.state.saved}
results={this.state.results}
handleSave={handleSave} />
</div>} {this.state.page === 'saved' &&
<div>
<ResultsList saved={this.state.saved}
results={this.state.saved}
handleSave={handleSave} />
</div>} {this.state.noResults && <div>{this.state.noResults}</div>}
</div>
);
}
}export default App;
A couple things to note:
- I’ve got a bunch of stuff in my
state. It’s messy and if I want to access those values from anything other thanApp.jsor one of its children, I’ll be screwed. - The “navigation” is such a mess — all those
this.state.page === 'saved'require tons of duplicate code. We’ll clean this up in a later post with React Router.
The Components
I broke out two components in order to keep my App.js “light”.
NasaQuery, just the search box:
import React from 'react';
import { Input } from 'reactstrap';export default ({query, handleSubmit, handleChange}) => {
return (
<div className="search-box">
<form onSubmit={handleSubmit}>
<Input value={query} placeholder='Search NASA'
onChange={handleChange} />
</form>
</div>
)
}
There’s not much to say here. We’re passing props (both values and methods) down from the parent component (App) down to the child (NasaQuery).
ResultsList:
import React from 'react';
import { Button } from 'reactstrap';export default ({saved, results, handleSave}) => {
return (
<div className="results">
{saved && <div>{saved.length} items saved</div>}
{results && results.map( result => (
<div key={result.href}>
<h3>{result.data[0].title}</h3>
<p>{result.data[0].description}</p>
<img src={result.links[0].href} className="nasaImage"/>
<br />
<Button color="success" onClick={() => handleSave(result)}>Save</Button>
<hr />
</div>
))}
</div>
)
}
Nasa is sending over the data in a format that requires us to look at the first data element. We could probably have formatted this better from the parent but it works fine.
What now?
At this point, we have an app! It searches NASA, and we can “save” items to read later. Of course, if we refresh the page, we lose everything. In later posts, I’ll add localStorage so we can keep track of articles beyond one refresh of the page. We’ll also add some logic for making the buttons change after you’ve saved an item (as in the screenshot) and also allow us to delete items from saved.
I branched off and made a copy of the app at this stage. You can see it on Github.
So what’s wrong with this app?
The short answer is: not that much. When you’re trying to learn these technologies, it’s often hard to get to a point where you need to refactor to use the more advanced libraries. Do we need Redux here? Not quite yet. The app functions more or less fine as-is. But imagine our app grows and we have several “pages” that do lots of things — perhaps we talk to other APIs or manage something like a user profile. Every component that needs to access the state of one of its “siblings” is going to have to get that information (values or methods) from higher up the chain. You can see how that would get messy, quickly. Enter Redux. We’ll add it in the next post.
