Step 8
Update a book
We just added a page to create a new book, but we also want to update an existing book.
Let's create our EditBook container
src/containers/EditBook.tsx
import React, { Component, ChangeEvent, FormEvent } from 'react';
import { fetchBook, updateBook } from '../actions';
import { RouteComponentProps } from 'react-router-dom';
import BookForm from '../components/BookForm';
type Props = RouteComponentProps<{ id: string }>;
export default class EditBookPage extends Component<Props> {
state = {
title: '',
author: '',
loading: true
};
async componentDidMount() {
try {
const {
match: {
params: { id }
}
} = this.props;
const book = await fetchBook(id);
this.setState({ ...book, loading: false });
} catch (error) {
console.error('Error fetching books:', error);
this.setState({ loading: false });
}
}
onChange = (e: ChangeEvent<HTMLInputElement>) =>
this.setState({ [e.target.name]: e.target.value });
onSubmit = async (e: FormEvent<HTMLFormElement>) => {
try {
e.preventDefault();
const {
match: {
params: { id }
},
history: { push }
} = this.props;
const { title, author } = this.state;
await updateBook(id, { title, author });
push('/books');
} catch (error) {
console.error('Error updating book:', error);
}
};
render() {
const { loading, title, author } = this.state;
if (loading) return <div>Loading...</div>;
if (!loading && !title && !author) return <div>Book not found!</div>;
return (
<div>
Update this book:
<BookForm
{...this.state}
onSubmit={this.onSubmit}
onChange={this.onChange}
/>
</div>
);
}
}
This combines the concepts we learned from our BookPage and CreateBookPage containers, so this should look familiar.
Now, we add the route to our App
src/App.tsx
import React, { Component } from 'react';
import './App.css';
import BooksPage from './containers/Books';
import { Switch, Route } from 'react-router-dom';
import HomePage from './containers/Home';
import Navbar from './components/Navbar';
import BookPage from './containers/Book';
import CreateBookPage from './containers/CreateBook';
import EditBookPage from './containers/EditBook';
class App extends Component {
render() {
return (
<div className="App App-header">
<Navbar />
<br />
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/books/create" component={CreateBookPage} />
<Route exact path="/books/:id/edit" component={EditBookPage} />
<Route exact path="/books/:id" component={BookPage} />
<Route exact path="/books" component={BooksPage} />
</Switch>
</div>
);
}
}
export default App;
We can also add a button to our Book page to edit it.
src/containers/Book.tsx
import React, { Component } from 'react';
import { fetchBook } from '../actions';
import Book from '../components/Book';
import { RouteComponentProps, Link } from 'react-router-dom';
import { IBook } from '../types';
type BookProps = RouteComponentProps<{ id: string }>;
type BookState = { book: IBook | null; loading: boolean };
export default class BookPage extends Component<BookProps, BookState> {
state = {
book: null,
loading: true
};
async componentDidMount() {
try {
const {
match: {
params: { id }
}
} = this.props;
const book = await fetchBook(id);
this.setState({ book, loading: false });
} catch (error) {
console.error('Error fetching book:', error);
this.setState({ loading: false });
}
}
render() {
const { book, loading } = this.state;
if (loading) return <div>Loading...</div>;
return (
<div>
The book you are looking for:
{book ? <Book book={book} /> : <div>Book not found!</div>}
<Link to={`/books/${this.props.match.params.id}/edit`}>
<button>Edit</button>
</Link>
</div>
);
}
}
Last updated
Was this helpful?