In my previous articles, I talked about how to build a dynamically controlled form on the frontend and how to save that data to the backend. In this last installment, I’ll go over the final piece to make this form possible: the API call and additional parts needed.
Posting a recipe
For simplicity’s sake, the API call to post a recipe is stored at the App level.
The constructor holds information for our user as well as our recipes.
constructor() {
super();
this.state = {
auth: {
user: {},
recipes: [],
}
};
}
Data from the form is stored in a newRecipe object. For additional security, the user_id could be changed to something else, such as their access token.
let newRecipe = {
title: newRecipeState.title,
summary: newRecipeState.summary,
ingredients: newRecipeState.ingredients,
steps: newRecipeState.steps,
tags: newRecipeState.tags,
user_id: this.state.auth.user.id
}
Then, that data is sent to the backend. I use a console.log to verify the data I’m working with when in the testing phase, but this should be taken out for the final product.
return fetch("http://localhost:3000/api/v1/recipes", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: localStorage.getItem("token")
},
body: JSON.stringify(newRecipe)
})
.then(resp => resp.json())
.then(data =>
console.log(data))
}
I’ve used the browser router library to display the different components and pass down props and functions in the render method.
<Route
exact
path='/add-recipe'
render={props => <AddRecipeForm {...props} onAddRecipe={this.addRecipe}/>}
/>
Editing a recipe
The editing API call follows similar logic. The recipe’s id is needed to update its information.
editRecipe = (recipe_id, editRecipeState) => {
let editedRecipe = {
title: editRecipeState.title,
summary: editRecipeState.summary,
ingredients: editRecipeState.ingredients,
steps: editRecipeState.steps,
tags: editRecipeState.tags,
user_id: this.state.auth.user.id
}
return fetch(`http://localhost:3000/api/v1/recipes/${recipe_id}`,{
method: "PATCH",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: localStorage.getItem("token")
},
body: JSON.stringify(editedRecipe)
}).then(resp => resp.json())
.then(data =>
(data))
}
In addition, the edit route also relies on the id path.
<Route
path='/recipes/edit/:id'
render={props => <EditForm {...props} appState={this.state} onEditRecipe = {this.editRecipe}/>}
/>
Now that we can post and edit a recipe, we ought to be able to view it as well. Although I originally wrote this series to specifically talk about form creation, it’s important to consider how all the pieces work together to create a functional site.
The good news is that viewing the recipes is the most straightforward part of the site. Write a fetch function to populate the state with recipes.
fetchRecipes = () =>{
api.recipes.getRecipes().then(data => {
this.setState({
recipes: data
})
})
}
Tie the fetch to componentDidMouth lifecycle method.
componentDidMount(){
this.fetchRecipes()
}
In addition, write the logic to render a list of recipes and a single recipe.
<Route
exact
path='/recipes'
render={props => <RecipeList {...props} appState={this.state}/>}
/>
<Route
exact
path='/recipes/:id'
render={props => <RecipeDetail {...props} appState={this.state}/>}
/>
Food for thought
I made this before learning about Redux and other state management tools. Hooks, for instance, could also make the codebase much cleaner and manageable. If you’re implementing a pure React app that uses classes, the previous functions provide a good starting point. However, the overall logic for creating a dynamically controlled form should remain the same no matter which tools you use.
Top comments (0)