In this project, we'll create an application to create and view recipe cards using React and Redux.
fork
andclone
this repository.cd
into the project root.- Run
npm install
to fetch the project dependencies. - Run
npm start
to spin up a development server.
In this step, we'll install some new dependencies, create a reducer, and create a Redux store.
- Install
redux
. - Open
/src/store.js
. - Import
createStore
from Redux. - Create an empty initial state object.
- Write a simple reducer. It should return state by default.
- Create and export a Redux store.
Detailed Instructions
In Redux, components need to connect to a store. Let's create this store. Open store.js
. We'll only be needing one thing from redux
: createStore
. createStore
does exactly what the name implies.
import { createStore } from "redux";
In order to create our store, we'll also need to create our initial state and reducer. Let's start with state. Our state will be an empty object for now.
const initialState = {};
Now that our initial state is set up, let's build a basic reducer
. The reducer
is a function that takes in two things: state
and an action
. Let's use our initialState
as the default value for our state
parameter.
function reducer(state = initialState, action) {}
Next we should build the switch
statement inside the reducer
. The switch
should test the type
property of the action
object. It should return state
unaltered as the default. Let's also destructure the action
object for easy access to its properties.
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
default:
return state;
}
}
Now that we have all the pieces we need, let's create and export our store
. We'll want to make this export the default.
export default createStore(reducer);
/src/store.js
import { createStore } from "redux";
const initialState = {};
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
default:
return state;
}
}
export default createStore(reducer);
In this step, we'll expand our reducer
so we can update the name and the category of our recipe.
- Add two properties to
initialState
instore.js
- One to store the recipe name.
- One to store the recipe category.
- Create two action type constants.
- One for for updating the recipe name.
- One for updating the recipe category.
- Remember to export them.
- Add two cases to the
switch
.- These should match the constants just created.
- These should alter the appropriate part of the state object and return the new state.
Detailed Instructions
Every time we want to store something in Redux state, we need to add it to the initialState
object with a default value. For our first set of inputs an empty string will work great.
const initialState = {
name: "",
category: ""
};
Now we need to create some action types. These should describe what the action will do. We need to export these so we can access them in all our components as well.
export const UPDATE_NAME = "UPDATE_NAME";
export const UPDATE_CATEGORY = "UPDATE_CATEGORY";
Next we need to tell the reducer
what to do with these actions when it recieves them. Let's add a case
for each of our actions
to our switch
. These should match the action types we just made.
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case UPDATE_NAME:
case UPDATE_CATEGORY:
default:
return state;
}
}
Each case should update the piece of state
that it needs to, and copy the rest of state
in an immutable way.
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case UPDATE_NAME:
return { ...state, name: payload };
case UPDATE_CATEGORY:
return { ...state, category: payload };
default:
return state;
}
}
/src/store.js
import { createStore } from "redux";
const initialState = {
name: "",
category: ""
};
export const UPDATE_NAME = "UPDATE_NAME";
export const UPDATE_CATEGORY = "UPDATE_CATEGORY";
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case UPDATE_NAME:
return { ...state, name: payload };
case UPDATE_CATEGORY:
return { ...state, category: payload };
default:
return state;
}
}
export default createStore(reducer);
In this step, we'll set up our first view, Name.js
, to use the action types we just added to store.js
.
- Open
/src/components/Name/Name.js
. - Import the
store
and the name and category action types from/src/store.js
. - Inside the
saveChanges
method, use thedispatch
method off of thestore
to send an action object.- The object should have a type that matches the name action type that was imported.
- The object should have a payload that sends the value of the name input box.
- Add another dispatch inside the
saveChanges
method for the category action type similar to the first.
Detailed Instructions
First we need to import the store
we created into this file, along with the action types we need for this component.
import store, { UPDATE_NAME, UPDATE_CATEGORY } from "./../../store.js";
The store
is an object with a method on it called dispatch
that we can use to send actions to the reducer
. We'll want to use this method twice, once for each piece of data that this component needs to save to Redux. We'll set these up inside the saveChanges
method that already fires when we click the Next
button.
saveChanges() {
store.dispatch();
store.dispatch();
}
Both of these dispatch
methods will send an action object to the reducer
. The type of the action objects should match the action types we imported above, and the payload should pull the values of the input boxes from state where they are being stored.
saveChanges() {
store.dispatch({
type: UPDATE_NAME,
payload: this.state.name
});
store.dispatch({
type: UPDATE_CATEGORY,
payload: this.state.category
});
}
/src/components/Name/Name.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import store, { UPDATE_NAME, UPDATE_CATEGORY } from "./../../store";
import "./Name.css";
class Name extends Component {
// Several lines for the constructor and handler methods omitted
saveChanges() {
store.dispatch({
type: UPDATE_NAME,
payload: this.state.name
});
store.dispatch({
type: UPDATE_CATEGORY,
payload: this.state.category
});
}
// Several lines for the render omitted
}
export default Name;
At this point, we can save the input values from Name.js
to Redux, but we aren't using that data to keep our input boxes from clearing. If we hit the Next
button, and then the Previous
button, the input boxes still forget what we typed in, so let's hook up our Name.js
component to use Redux state to remember.
- Inside the
constructor
, use thegetState
method that lives onstore
.- Store the return value in a
const
so we can reference it later.
- Store the return value in a
- Now change the initial state to use the appropriate values off of Redux state instead of empty strings.
- This means that when the component first mounts, it will pull the data we saved earlier.
Detailed Instructions
The store
is an object with a method on it called getState
that we can use to access the Redux state object. We'll invoke this method inside our constructor
and store the return value in a constant so we can reference it easily.
const reduxState = store.getState();
The reason we are invoking this method in the constructor
is so we can use the value in our component's initial state. We will reference the appropriate properties off of the Redux state to replace the empty strings that are in the component's state right now.
this.state = {
name: reduxState.name,
category: reduxState.category
};
Now when we flip between our pages, we should see our values persist on the Name.js
view.
/src/components/Name/Name.js
// imports omitted
class Name extends Component {
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
name: reduxState.name,
category: reduxState.category
};
}
// methods and render omitted
}
export default Name;
In this step, we are going to repeat all the setup we did for Name.js
for Author.js
.
- Open
/src/store.js
. - Add two properties to
initialState
.- One to store the author's first name.
- One to store the author's last name.
- Create and export two constants to match.
- Add two corresponding cases to the
switch
. - Open
/src/components/Author/Author.js
. - Import the
store
and the first and last name action types from/src/store.js
. - Inside the
saveChanges
method, usedispatch
(found on thestore
) twice, to send two seperate action objects.- The action objects should use the action types that were imported.
- They should pull the appropriate data from state for the payload.
- Inside the
constructor
, invoke thegetState
method (found on thestore
) and use the appropriate values from Redux state inside the component's initial state.
Detailed Instructions
First we need to add new properties to the initial Redux state.
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: ""
};
Now we create our action types
export const UPDATE_AUTHOR_FIRST = "UPDATE_AUTHOR_FIRST";
export const UPDATE_AUTHOR_LAST = "UPDATE_AUTHOR_LAST";
Next we need to tell the reducer
what to do with these action
objects when it recieves them. Let's add a case
for each action
to our switch. They should match the types we just made, and they should update the piece of state
that they need to, and copy the rest of state
in an immutable way.
case UPDATE_AUTHOR_FIRST:
return { ...state, authorFirst: payload };
case UPDATE_AUTHOR_LAST:
return { ...state, authorLast: payload };
Open up Author.js
. Import the store into this file, along with the action types we need for this component.
import store, { UPDATE_AUTHOR_FIRST, UPDATE_AUTHOR_LAST } from "./../../store";
Just like we did in Name.js
, we need to use the dispatch
method twice inside the saveChanges
method that already fires when we click the Next
or Previous
buttons. The type
of the action
objects used in dispatch
should match the action
types we imported above, and the payload
should pull the values from the component's state.
saveChanges() {
store.dispatch({
type: UPDATE_AUTHOR_FIRST,
payload: this.state.authorFirst
});
store.dispatch({
type: UPDATE_AUTHOR_LAST,
payload: this.state.authorLast
});
}
At this point we're saving the data, but we're not using it yet. Invoke the getState
method in the constructor and store the return value in a constant. Now reference the appropriate values off of Redux state to replace the empty strings in the component's initial state.
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
authorFirst: reduxState.authorFirst,
authorLast: reduxState.authorLast
};
}
Now when we flip between our pages, we should see our values persist on the Name.js
and the Author.js
view.
/src/store.js
import { createStore } from "redux";
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: ""
};
export const UPDATE_NAME = "UPDATE_NAME";
export const UPDATE_CATEGORY = "UPDATE_CATEGORY";
export const UPDATE_AUTHOR_FIRST = "UPDATE_AUTHOR_FIRST";
export const UPDATE_AUTHOR_LAST = "UPDATE_AUTHOR_LAST";
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case UPDATE_NAME:
return { ...state, name: payload };
case UPDATE_CATEGORY:
return { ...state, category: payload };
case UPDATE_AUTHOR_FIRST:
return { ...state, authorFirst: payload };
case UPDATE_AUTHOR_LAST:
return { ...state, authorLast: payload };
default:
return state;
}
}
export default createStore(reducer);
/src/components/Author/Author.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import store, { UPDATE_AUTHOR_FIRST, UPDATE_AUTHOR_LAST } from "./../../store";
import "./Author.css";
class Author extends Component {
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
authorFirst: reduxState.authorFirst,
authorLast: reduxState.authorLast
};
}
// handler methods omitted
saveChanges() {
store.dispatch({
type: UPDATE_AUTHOR_FIRST,
payload: this.state.authorFirst
});
store.dispatch({
type: UPDATE_AUTHOR_LAST,
payload: this.state.authorLast
});
}
// render omitted
}
export default Author;
In this step, we'll set up Ingredients.js
much the same way we have Name.js
and Author.js
, but we'll need to add one more piece. The Ingredients.js
view needs to update Redux before we navigate to a new page, so we need to be able to pull data from Redux whenever there are changes, not just in the constructor
.
- Open
/src/store.js
. - Add a property to
initialState
to store the list of ingredients. - Create and export a constant to match.
- Add a
case
to theswitch
. - Open
/src/components/Ingredients/Ingredients.js
. - Import the
store
and the ingredients action type from/src/store.js
. - Inside the
addIngredient
method, usedispatch
(found on thestore
) to send an action object.- It should use the action type that was imported.
- It should pull the input data from state for the
payload
.
- Inside the
constructor
, invoke thegetState
method (found on thestore
) and use the appropriate value from Redux state inside the component's initial state. - Now for the new part! Inside of
componentDidMount
, use thesubscribe
method that lives onstore
.subscribe
takes a callback function as its argument.- This callback should invoke
getState
just like the constructor does. - Then it should call
this.setState
and use the value from Redux state to update the component's state.
Detailed instructions
First we need to add a new property to the initial Redux state, create an action type, and add a case to our reducer
. This case will look a little more complicated than the ones we've done before because we're working with a list now, so we'll need to make a copy of that list before making changes.
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: "",
ingredients: []
}
export const ADD_INGREDIENT = "ADD_INGREDIENT";
case ADD_INGREDIENT:
const newIngredients = [...state.ingredients, payload];
return { ...state, ingredients: newIngredients };
Open up Ingredients.js
. Import the store
into this file, along with the action type.
import store, { ADD_INGREDIENT } from "./../../store";
Just like we did before, we need to use the dispatch
method. This time we only need to use it once, and it should go inside addIngredient
. The type
of the action
object used in dispatch
should match what we imported above, and the payload
should pull the input value from state.
addIngredient() {
store.dispatch({
type: ADD_INGREDIENT,
payload: this.state.input
});
this.setState({
input: ""
});
}
Now just like before, we need to set up the constructor
to pull in its initial state from Redux state.
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
ingredients: reduxState.ingredients,
input: ""
};
}
Now we're really close. At this point we're saving our data on Redux, and we can see it show up, but only if we navigate away from the page and back again. So now we just need to make our list show up without leaving the page.
First we need to create a componentDidMount
method for this component. Inside this method we are going to use another piece that comes from store
. This one is called subscribe
. subcribe
allows us to update our page any time the data on Redux state changes.
subscribe
takes a callback function as its argument that will fire any time there is an update in Redux. So every time this function fires we want to use getState
to get an updated version of the Redux state. Then we'll use this.setState
to update our component's state with the new values.
componentDidMount() {
store.subscribe(() => {
const reduxState = store.getState();
this.setState({
ingredients: reduxState.ingredients
});
});
}
Now when we are on the Ingredients.js
we can see our list update, and when we flip between our pages, we should see our values persist on the Name.js
, the Author.js
, and the Ingredients.js
view.
/src/store.js
import { createStore } from "redux";
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: "",
ingredients: []
};
export const UPDATE_NAME = "UPDATE_NAME";
export const UPDATE_CATEGORY = "UPDATE_CATEGORY";
export const UPDATE_AUTHOR_FIRST = "UPDATE_AUTHOR_FIRST";
export const UPDATE_AUTHOR_LAST = "UPDATE_AUTHOR_LAST";
export const ADD_INGREDIENT = "ADD_INGREDIENT";
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case UPDATE_NAME:
return { ...state, name: payload };
case UPDATE_CATEGORY:
return { ...state, category: payload };
case UPDATE_AUTHOR_FIRST:
return { ...state, authorFirst: payload };
case UPDATE_AUTHOR_LAST:
return { ...state, authorLast: payload };
case ADD_INGREDIENT:
const newIngredients = [...state.ingredients, payload];
return { ...state, ingredients: newIngredients };
default:
return state;
}
}
export default createStore(reducer);
/src/components/Ingredients/Ingredients.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import store, { ADD_INGREDIENT } from "./../../store";
class Ingredients extends Component {
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
ingredients: reduxState.ingredients,
input: ""
};
}
componentDidMount() {
store.subscribe(() => {
const reduxState = store.getState();
this.setState({
ingredients: reduxState.ingredients
});
});
}
// handler method omitted
addIngredient() {
store.dispatch({
type: ADD_INGREDIENT,
payload: this.state.input
});
this.setState({
input: ""
});
}
// render omitted
}
export default Ingredients;
In this step, we'll set up Instructions.js
like we have Ingredients.js
.
- Open
/src/store.js
. - Add a property to
initialState
to store the list of instructions. - Create and export a constant to match.
- Add a
case
to theswitch
. - Open
/src/components/Instructions/Instructions.js
. - Import the
store
and the instructions action type from/src/store.js
. - Inside the
addInstruction
method, usedispatch
(found on thestore
) to send an action object.- It should use the action type that was imported.
- It should pull the input data from state for the
payload
.
- Inside the
constructor
, invoke thegetState
method (found on thestore
) and use the appropriate value from Redux state inside the component's initial state. - Inside of
componentDidMount
, use thesubscribe
method (found on thestore
).- The callback for
subscribe
should invokegetState
just like the constructor does. - Then it should call
this.setState
and use the value from Redux state to update the component's state.
- The callback for
Detailed Instructions
First we need to add a new property to the initial Redux state, create an action type, and add a case
to our reducer
. This case
will be very similar to the one we created for Ingredients.js
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: "",
ingredients: [],
instructions: []
}
export const ADD_INSTRUCTION = "ADD_INSTRUCTION";
case ADD_INSTRUCTION:
const newInstructions = [...state.instructions, payload];
return { ...state, instructions: newInstructions };
Open up Instructions.js
. Import the store
into this file, along with the action type.
import store, { ADD_INSTRUCTION } from "./../../store";
Just like we did before, we need to use the dispatch
method inside addInstruction
. The type
of the action object used in dispatch
should match the action type we imported above, and the payload
should pull the input value from state.
addInstruction() {
store.dispatch({
type: ADD_INSTRUCTION,
payload: this.state.input
});
this.setState({
input: ""
});
}
Now just like before, we need to set up the constructor
to pull in its initial state from Redux state.
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
instructions: reduxState.instructions,
input: ""
};
}
Next we need to listen to changes using subscribe
like we did in Ingredients.js
. Create a componentDidMount
method for this component. subscribe
goes inside this method. The callback function that we pass in should use getState
to get an updated version of the Redux state. Then we'll use this.setState
to update our component's state with the new values.
componentDidMount() {
store.subscribe(() => {
const reduxState = store.getState();
this.setState({
instructions: reduxState.instructions
});
});
}
Now we should see our lists update on both the Ingredients.js
and Instructions.js
pages. When we flip between our pages, we should see our values persist on the Name.js
, the Author.js
, the Ingredients.js
, and the Instructions.js
view.
/src/store.js
import { createStore } from "redux";
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: "",
ingredients: [],
instructions: []
};
export const UPDATE_NAME = "UPDATE_NAME";
export const UPDATE_CATEGORY = "UPDATE_CATEGORY";
export const UPDATE_AUTHOR_FIRST = "UPDATE_AUTHOR_FIRST";
export const UPDATE_AUTHOR_LAST = "UPDATE_AUTHOR_LAST";
export const ADD_INGREDIENT = "ADD_INGREDIENT";
export const ADD_INSTRUCTION = "ADD_INSTRUCTION";
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case UPDATE_NAME:
return { ...state, name: payload };
case UPDATE_CATEGORY:
return { ...state, category: payload };
case UPDATE_AUTHOR_FIRST:
return { ...state, authorFirst: payload };
case UPDATE_AUTHOR_LAST:
return { ...state, authorLast: payload };
case ADD_INGREDIENT:
const newIngredients = [...state.ingredients, payload];
return { ...state, ingredients: newIngredients };
case ADD_INSTRUCTION:
const newInstructions = [...state.instructions, payload];
return { ...state, instructions: newInstructions };
default:
return state;
}
}
export default createStore(reducer);
/src/components/Instructions/Instructions.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import store, { ADD_INSTRUCTION } from "./../../store";
class Instructions extends Component {
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
instructions: reduxState.instructions,
input: ""
};
}
componentDidMount() {
store.subscribe(() => {
const reduxState = store.getState();
this.setState({
instructions: reduxState.instructions
});
});
}
// handler method omitted
addInstruction() {
store.dispatch({
type: ADD_INSTRUCTION,
payload: this.state.input
});
this.setState({
input: ""
});
}
// create method & render omitted
}
export default Instructions;
In this step, we'll use all the values we've saved on Redux to create a recipe.
- Open
/src/store.js
. - Add a property to
initialState
to store the list of recipes. - Create and export a constant to match.
- Add a
case
to theswitch
. Thiscase
should use the values already stored on state to create the new recipe and won't rely on apayload
. - Open
/src/components/Instructions/Instructions.js
. - Import the recipe action type from
/src/store.js
. - Inside the
create
method, usedispatch
(found on thestore
) to send an action object.- It should use the action type that was imported.
- It should not include a
payload
.
Detailed Instructions
First we need to add a new property to the initial Redux state and create an action type
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: "",
ingredients: [],
instructions: [],
recipes: []
};
export const ADD_RECIPE = "ADD_RECIPE";
Now we'll add a case
to our reducer
. This case
will be quite a bit different from what we've done so far, because it doesn't use a payload
. Payloads are really useful when we need to transfer data from a component to Redux, but in this circumstance all the data is already being stored in Redux. So we'll pull all the values we've been storing so far off of state
and build a recipe object with it. Then we we'll want to copy our list of recipes and add our a new recipe to it. Then of course we need to copy the rest of state
in an immutable way.
case ADD_RECIPE:
const {
name,
category,
authorFirst,
authorLast,
ingredients,
instructions
} = state;
const recipe = {
name,
category,
authorFirst,
authorLast,
ingredients,
instructions
};
const newRecipes = [...state.recipes, recipe];
return { ...state, recipes: newRecipes };
Open up Instructions.js
. Import the recipe action type.
import store, { ADD_INSTRUCTION, ADD_RECIPE } from "./../../store";
We need to use the dispatch
method again, this time inside create
. The type
of the action object used in dispatch should match the action type we just imported, and there shouldn't be a payload
.
create() {
store.dispatch({
type: ADD_RECIPE
});
}
Now when we click the Create
button, we will actually create a new recipe! (It just doesn't display anywhere yet).
/src/store.js
import { createStore } from "redux";
const initialState = {
name: "",
category: "",
authorFirst: "",
authorLast: "",
ingredients: [],
instructions: [],
recipes: []
};
export const UPDATE_NAME = "UPDATE_NAME";
export const UPDATE_CATEGORY = "UPDATE_CATEGORY";
export const UPDATE_AUTHOR_FIRST = "UPDATE_AUTHOR_FIRST";
export const UPDATE_AUTHOR_LAST = "UPDATE_AUTHOR_LAST";
export const ADD_INGREDIENT = "ADD_INGREDIENT";
export const ADD_INSTRUCTION = "ADD_INSTRUCTION";
export const ADD_RECIPE = "ADD_RECIPE";
function reducer(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case UPDATE_NAME:
return { ...state, name: payload };
case UPDATE_CATEGORY:
return { ...state, category: payload };
case UPDATE_AUTHOR_FIRST:
return { ...state, authorFirst: payload };
case UPDATE_AUTHOR_LAST:
return { ...state, authorLast: payload };
case ADD_INGREDIENT:
const newIngredients = [...state.ingredients, payload];
return { ...state, ingredients: newIngredients };
case ADD_INSTRUCTION:
const newInstructions = [...state.instructions, payload];
return { ...state, instructions: newInstructions };
case ADD_RECIPE:
const {
name,
category,
authorFirst,
authorLast,
ingredients,
instructions
} = state;
const recipe = {
name,
category,
authorFirst,
authorLast,
ingredients,
instructions
};
const newRecipes = [...state.recipes, recipe];
return { ...state, recipes: newRecipes };
default:
return state;
}
}
export default createStore(reducer);
/src/components/Instructions/Instructions.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import store, { ADD_INSTRUCTION, ADD_RECIPE } from "./../../store";
class Instructions extends Component {
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
instructions: reduxState.instructions,
input: ""
};
}
componentDidMount() {
store.subscribe(() => {
const reduxState = store.getState();
this.setState({
instructions: reduxState.instructions
});
});
}
// handler method omitted
addInstruction() {
store.dispatch({
type: ADD_INSTRUCTION,
payload: this.state.input
});
this.setState({
input: ""
});
}
create() {
store.dispatch({
type: ADD_RECIPE
});
}
// render omitted
}
export default Instructions;
Finally we'll get our recipes to display in Home.js
.
- Open
/src/components/Home/Home.js
. - Import the
store
from/src/store.js
. - Inside the
constructor
, invoke thegetState
method (found on thestore
) and use the appropriate value from Redux state inside the component's initial state.
Detailed Instructions
We are almost done! All our data is being saved correctly; we just have to display our finished recipes.
First we need to import the store
in Home.js
import store from "./../../store";
Now we need to use getState
once again to pull the data from Redux state for the component's initial state
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
recipes: reduxState.recipes
};
}
And we are done! Any recipe you create should show up on the Home.js
view.
/src/components/Home/Home.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import RecipeCard from "./../RecipeCard/RecipeCard";
import store from "./../../store";
import "./Home.css";
class Home extends Component {
constructor(props) {
super(props);
const reduxState = store.getState();
this.state = {
recipes: reduxState.recipes
};
}
// render omitted
}
export default Home;
- When we create a recipe, the fields in our form don't clear. Create another action type to clear the fields when we create a new recipe.
- Right now each recipe displays a delete button that doesn't do anything. Create another action type to delete a recipe and hook it up to the delete button.
Redux
- Getting Started
- Core Concepts
- Basic Tutorial Note: Each section covers a foundational redux concept. Stop at the Usage With React section. We'll cover that in a future lecture.
Object Rest Spread
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.