M dunbavan M dunbavan - 1 month ago 9
React JSX Question

How can I access a single view in on a SPA?

I have a react-js application with the following structure:

public
src
components
- Books.js
- BookShow.js
- App.css
- App.js
- index.js


In my package.json I am using the following node modules:

{
"name": "tester",
"version": "0.1.0",
"private": true,
"devDependencies": {
"react-scripts": "0.7.0"
},
"dependencies": {
"bootstrap": "^3.3.7",
"react": "^15.3.2",
"react-bootstrap": "^0.30.6",
"react-dom": "^15.3.2",
"react-router": "^3.0.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}


In my main site I am basically trying to create a 1 page app where we list books from a books.json file. I can do this and I list the books very easily in my Books.js file with the following:

import React from 'react';
import { Link } from 'react-router';
export default React.createClass({


getInitialState: function() {
return {
books: []
}
},

componentDidMount: function() {
this.request = fetch('https://gist.githubusercontent.com/madwork/adfae25c174bb246c650/raw/db225e4b9c74e1865da1c9cd54cf87d3dee171a5/book.json')
.then((request) => request.json())
.then((data) => this.setState({
books: data,
isLoading: false
}))
},

componentWillUnmount: function() {
this.serverRequest.abort();
},

render: function() {
return (
<div className="container">
<h1>Books</h1>
{this.state.books.map(function(book) {
return (
<div key={book.id} className="book col-lg-4 col-md-4 col-sm-4">
<Link
to={"/books/"+book.id}
className="list-group-item"
key={book.id}>
{book.name}
<img className="img-responsive" src={book.cover} />
</Link>
<h3>{book.name}</h3>
<p>{book.author.name}</p>
<i className="glyphicon glyphicon-heart">{book.likes}</i>
</div>
);
})}
</div>

)
}

})


In my main App.js file and my index.js files I have the routing setup as follows:

App.js

import { Link } from 'react-router';
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {

render() {

return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<ul>
<li><Link activeStyle={{ color: 'red' }} to="/books">Books</Link></li>
</ul>

{this.props.children}

</div>
);
}
}


export default App;


index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { render } from 'react-dom'
import { Router, Route, IndexRoute, browserHistory, hashHistory } from 'react-router'
import App from './App'
import Books from './components/Books'
import BookShow from './components/BookShow'
import './index.css'
import 'bootstrap/dist/css/bootstrap.css'



render((
<Router history={hashHistory}>
<Route path='/' component={App}>
<Route path='/books' component={Books}>
<Route path='/books/:id' component={BookShow}/>
</Route>
</Route>
</Router>
), document.getElementById('root'))


What I am really struggling to do is enable the user to click into the book object from the list and get the single view of this book with all its data. I have set the routes up as I see logically for but I am wondering where I am missing the trick here.

Can anyone help point me in the right place or equally shout at me if this is all wrong :)

Mark

Answer

The problem is, that your data lives inside your Books Component. And you don't have a data layer which is component agnostic.

There are different approaches to it, like Redux, which will hold all of your data inside a global store.

Another approach would be to build your own data layer.. It could be something simple, like an object which gets filled by your api methods. So your api methods could look for cached items before sending requests to your backend.

The are unlimited options, actually.. You could also just call you backend each time some one visits the book/:id page, and load the book.

So, it depends on your knowledge, time.. and effort you want to spend. Every solution has its own benefits and drawbacks.


Personally, I would first create a separate api module, which would perform the api calls. And use some global storage for the data. And if the data is almost statical, I would try to find the book first locally, and if its not there call the backend -> store it locally -> give it back to the component.

In some cases its nice to use Redux, in some its an overhead. However, its worth to try it out.