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)