React - Add user to list of Favorites - javascript

I have a simple user list with several details from the following api: https://gorest.co.in/public-api/users, where I want to add a selected user to a list of favorites. I am working with react-router to navigate between pages. Is this possible with React or do I also need Redux?
I have a complete LIVE EXAMPLE here with the user page and favorites.
Here is the code below for the user list:
import React from "react";
import axios from "axios";
import NavLinks from "./components/navLink";
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [],
addToFav: false
};
this.list = [];
}
componentDidMount() {
this.getList();
}
/* get users list */
getList = async () => {
const api =
"https://gorest.co.in/public-api/users?_format=json&access-token=3qIi1MDfD-GXqOSwEHHLH73Y3UitdaFKyVm_";
await axios
.get(api)
.then(response => {
this.list = response.data.result;
this.setState({
list: this.list
});
})
.catch(err => {
console.log(err);
});
};
addToFav = () => {
this.setState(
{
addToFav: !this.state.addToFav
},
() => console.log(this.state.addToFav)
);
};
render() {
let style = {
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(250px, 1fr))",
padding: "1rem",
gridGap: "1rem 1rem"
};
return (
<div>
<NavLinks />
<ul style={style}>
{this.state.list.map(user => {
return (
<li key={user.id}>
<div>
<img className="thumb" alt="" src={user._links.avatar.href} />
</div>
<div className="userInfo">
<p>
{user.first_name} {user.last_name}
</p>
</div>
<button onClick={this.addToFav}>Add to Favorites</button>
</li>
);
})}
</ul>
</div>
);
}
}
Thank you!

Here's a working codesandbox: https://codesandbox.io/s/brave-fire-4kd4p
This train of thought pretty much follows what #Chris G mentioned. Have a top-level state that holds the list of users and the favorites list. Then pass those as props to the individual components.
App.js
Hit your API here instead of inside your UserList component to prevent any unnecessary re-renders.
import React, { Component } from "react";
import UserList from "./userList";
import FavoriteList from "./favoriteList";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import axios from "axios";
export default class App extends Component {
state = {
list: [],
favorites: []
};
addFavorite = favorite => {
const { favorites } = this.state;
if (!favorites.some(alreadyFavorite => alreadyFavorite.id == favorite.id)) {
this.setState({
favorites: [...this.state.favorites, favorite]
});
}
};
getList = async () => {
const api =
"https://gorest.co.in/public-api/users?_format=json&access-token=3qIi1MDfD-GXqOSwEHHLH73Y3UitdaFKyVm_";
await axios
.get(api)
.then(response => {
this.setState({
list: response.data.result
});
})
.catch(err => {
console.log(err);
});
};
componentDidMount() {
this.getList();
}
render() {
return (
<Router>
<Switch>
<Route
path="/"
exact
render={() => (
<UserList list={this.state.list} addFavorite={this.addFavorite} />
)}
/>
<Route
path="/favorites"
render={() => <FavoriteList favorites={this.state.favorites} />}
/>
</Switch>
</Router>
);
}
}
UserList.js
Call the addFavorite event-handler on button-click to pass that item back up to the parent-state.
import React from "react";
import NavLinks from "./components/navLink";
export default class UserList extends React.Component {
render() {
let style = {
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(250px, 1fr))",
padding: "1rem",
gridGap: "1rem 1rem"
};
return (
<div>
<NavLinks />
<ul style={style}>
{this.props.list.map(user => {
return (
<li key={user.id}>
<div>
<img className="thumb" alt="" src={user._links.avatar.href} />
</div>
<div className="userInfo">
<p>
{user.first_name} {user.last_name}
</p>
</div>
<button onClick={() => this.props.addFavorite(user)}>
Add to Favorites
</button>
</li>
);
})}
</ul>
</div>
);
}
}
Favorite.js
Use the favorites array that was passed in as a prop and iterate over it.
import React from "react";
import NavLinks from "./components/navLink";
export default class FavoriteList extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { favorites } = this.props;
return (
<div>
<NavLinks />
<ul>
{favorites.map(user => {
return (
<li key={user.id}>
<div>
<img className="thumb" alt="" src={user._links.avatar.href} />
</div>
<div className="userInfo">
<p>
{user.first_name} {user.last_name}
</p>
</div>
</li>
);
})}
</ul>
</div>
);
}
}

Is this possible with React or do I also need Redux?
Most if not all of those problems can be solved without redux just by using component state. It just gets increasingly difficult to pass the state to the components needing it the more global state you have and the more components at different depth need to access and update it.
In your case it might be sufficient to store the favorites in a component state high up the tree and pass it to the components consuming it. You could either pass it directly to the components or you could use react context to make it accessible to components deep in the tree.
A simple example:
const FavoritesContext = React.createContext({favorites: []});
const FavoritesProvider = ({children}) => {
const [favorites, setFavorites] = useState([]);
const add = useCallback(favorite => setFavorites(current => [...current, favorite]), [setFavorites]);
return (
<FavoritesContext.Provider value={{favorites, add}}>
{children}
</FavoritesContext.Provider>
};
You can use it like that:
<FavoritesProvider>
<MyApp />
</FavoritesProvider>
then anywhere in a component in your app:
const MyComponent = () => {
const {favorites, add} = useContext(FavoritesContext);
const [draft, setDraft] = useState('');
const handleChange = event => setDraft(event.target.value);
const handleAdd = () => {
add(draft);
setDraft('');
};
return (
<div>
<ul>
{favorites.map(favorite => <li>{favorite}</li>)}
</ul>
<input value={draft} type="text" onChange={handleChange} />
<button onClick={handleAdd}>Add</button>
</div>
);
}
In this simple example the favorites are just text but they could as well be objects. Also it demonstrates how you could provide a handler for adding a favorite. You could implement e.g. a handler for removing favorites in the same way.
Persisting your favorites is yet another topic you may need to deal with. You could use e.g. localStorage for that or you could store that in a database on a server and fetch it when your app mounts the first time.

I have changed your file a bit take a look - https://codesandbox.io/s/clever-butterfly-vb2iz

One way is to use the localstorage of browser.
But this way is slighty expensive and synchronous.
Update the list whenever the favorited item status is changed via
localStorage.setItem('users',JSON.stringify(users));
And look for the favorited items via
localStorage.getItem('users');//You need to parse this by JSON.parse()
Maintain a isFavorite variable in the object list.
let users=[{name:"Mr.A",isFavorite:false},{name:"Mr.B",isFavorite:true},...];
On the click of favoriting button this.addToFav change it as follows
addToFav=user=>{
const {users}=this.state;
this.setState({
users:users.map(userObject=>userObject.id===user.id?
{...userObject,isFavorite:!userObject.isFavorite}:user)
},()=>{saveToLocal(this.state.users)});
}
Now you can access the favorite items even if the page is reloaded and stays there till you clear the storage.Use this localStorage.clear() for that.

First I would change your onClick to this:
<button onClick={() => this.addToFav(user.id)}>Add to Favorites</button>
This will allow you to pass the id to the addToFave function.
Then I would add a new state called faves (an array) and every time someone clicks the add button I would add their id into this array. This will allow you to filter your original list when you want to display the faves.
this.state = {
list: [],
faves: [],
};
}
addToFav = (id) => {
this.setState(prevState => ({
faves: [...prevState.faves, id],
}));
};
When I want to use the list of faves instead of the normal list I would do this:
const favesList = [];
this.state.list.map(listItem =>
this.state.faves.find(
faveId => listItem.id === faveId
) && favesList.push(item);
Then I would pass that to the faves component

I changed accordingly, please try
https://codesandbox.io/s/youthful-poincare-7oeh0
the key is you can use push state to your link like below
<Link to={{ pathname: "/favorites", state: { favList: this.props.favList }}} onClick={() => this.forceUpdate()}>
later on under your fav page call to retrieve the state
this.props.location.state.favList

i have changed the code a little by using react context.
I would not use redux for this cause i think it would be a overkill.
Anyways here is the updated sandbox...
Link for sandbox

Related

React child callback not being executed after being passed down twice

I am working on the following project https://github.com/codyc4321/react-udemy-course section 11 the videos app. The udemy course is found at https://www.udemy.com/course/react-redux/learn/lecture/12531374#overview.
The instructor is passing a callback down to multiple children and calling it in the lowest videoItem and the code is supposed to console log something out. I have no console log in my browser even though I've copied the code as written and double checked for spelling errors.
At the main level is App.js:
import React from 'react';
import youtube from '../apis/youtube';
import SearchBar from './SearchBar';
import VideoList from './VideoList';
class App extends React.Component {
state = {videos: [], selectedVideo: null};
onTermSubmit = async term => {
const response = await youtube.get('/search', {
params: {
q: term
}
});
// console.log(response.data.items);
this.setState({videos: response.data.items});
};
onVideoSelect = video => {
console.log('from the app', video);
}
render() {
return (
<div className="ui container">
<SearchBar onFormSubmit={this.onTermSubmit} />
<VideoList
onVideoSelect={this.onVideoSelect}
videos={this.state.videos} />
</div>
)
}
}
export default App;
videoList.js
import React from 'react';
import VideoItem from './VideoItem';
const VideoList = ({videos, onVideoSelect}) => {
const rendered_list = videos.map(video => {
return <VideoItem onVideoSelect={onVideoSelect} video={video} />
});
return <div className="ui relaxed divided list">{rendered_list}</div>;
};
export default VideoList;
the videoItem.js
import React from 'react';
import './VideoItem.css';
const VideoItem = ({video, onVideoSelect}) => {
return (
<div onClick={() => onVideoSelect(video)} className="item video-item">
<img
src={video.snippet.thumbnails.medium.url}
className="ui image"
/>
<div className="content">
<div className="header">{video.snippet.title}</div>
</div>
</div>
);
}
export default VideoItem;
The code that isn't running is
onVideoSelect = video => {
console.log('from the app', video);
}
My guess is that it has something to do with a key prop not being present in the map - I'm not super well versed with class components but I can't find anything else funky so maybe try adding a unique key prop in the map.
When rendering components through a map react needs help with assigning unique identifiers to keep track of re-renders etc for performance, that also applies to knowing which specific instance called a class method.
If you don't have a unique ID in the video prop you can use an index in a pinch, although ill advised, it can be found as the second parameter in the map function. The reason it's ill advised to use an index is if there are multiple children with the same index in the same rendering context, obviously the key parameter could be confused.
Okay-ish:
const rendered_list = videos.map((video, index) => {
return <VideoItem key={index} onVideoSelect={onVideoSelect} video={video} />});
Better:
const rendered_list = videos.map((video, index) => {
return <VideoItem key={video.id} onVideoSelect={onVideoSelect} video={video} />});

React: Persisting State Using Local Storage

I am coding an app in which there is a collection of reviews and a person can respond to a review, but each review can only have one response. So far, I am doing this by rendering a ReviewResponseBox component in my ReviewCardDetails component and passing the review_id as props.
I have implemented the logic so that once there is one ReviewResponse, the form to write another will no longer appear. However, before I was initializing the state in this component with an empty array, so when I refreshed my page the response went away and the form came back up. (This is now commented out)
I am trying to resolve this by persisting my state using React LocalStorage but am having trouble writing my method to do this. Here is what I have so far:
Component that renders ReviewResponseBox and passes review_id as props:
import React from "react";
import './Review.css';
import { useLocation } from "react-router-dom";
import StarRatings from "react-star-ratings";
import ReviewResponseBox from "../ReviewResponse/ReviewResponseBox";
const ReviewCardDetails = () => {
const location = useLocation();
const { review } = location?.state; // ? - optional chaining
console.log("history location details: ", location);
return (
<div key={review.id} className="card-deck">
<div className="card">
<div>
<div className='card-container'>
<h4 className="card-title">{review.place}</h4>
<StarRatings
rating={review.rating}
starRatedColor="gold"
starDimension="20px"
/>
<div className="card-body">{review.content}</div>
<div className="card-footer">
{review.author} - {review.published_at}
</div>
</div>
</div>
</div>
<br></br>
<ReviewResponseBox review_id={review.id}/>
</div>
);
};
export default ReviewCardDetails;
component that I want to keep track of the state so that it can render the form or response:
import React from 'react';
import ReviewResponse from './ReviewResponse';
import ReviewResponseForm from './ReviewResponseForm';
import { reactLocalStorage } from "reactjs-localstorage";
class ReviewResponseBox extends React.Component {
// constructor() {
// super()
// this.state = {
// reviewResponses: []
// };
// }
fetchResponses = () => {
let reviewResponses = [];
localStorage.setResponses
reviewResponses.push(reviewResponse);
}
render () {
const reviewResponses = this.getResponses();
const reviewResponseNodes = <div className="reviewResponse-list">{reviewResponses}</div>;
return(
<div className="reviewResponse-box">
{reviewResponses.length
? (
<>
{reviewResponseNodes}
</>
)
: (
<ReviewResponseForm addResponse={this.addResponse.bind(this)}/>
)}
</div>
);
}
addResponse(review_id, author, body) {
const reviewResponse = {
review_id,
author,
body
};
this.setState({ reviewResponses: this.state.reviewResponses.concat([reviewResponse]) }); // *new array references help React stay fast, so concat works better than push here.
}
getResponses() {
return this.state.reviewResponses.map((reviewResponse) => {
return (
<ReviewResponse
author={reviewResponse.author}
body={reviewResponse.body}
review_id={this.state.review_id} />
);
});
}
}
export default ReviewResponseBox;
Any guidance would be appreciated
You would persist the responses to localStorage when they are updated in state using the componentDidUpdate lifecycle method. Use the componentDidMount lifecycle method to read in the localStorage value and set the local component state, or since reading from localStorage is synchronous directly set the initial state.
I don't think you need a separate package to handle this either, you can use the localStorage API easily.
import React from "react";
import ReviewResponse from "./ReviewResponse";
import ReviewResponseForm from "./ReviewResponseForm";
class ReviewResponseBox extends React.Component {
state = {
reviewResponses: JSON.parse(localStorage.getItem(`reviewResponses-${this.props.review_id}`)) || []
};
storageKey = () => `reviewResponses-${this.props.review_id}`;
componentDidUpdate(prevProps, prevState) {
if (prevState.reviewResponses !== this.state.reviewResponses) {
localStorage.setItem(
`reviewResponses-${this.props.review_id}`,
JSON.stringify(this.state.reviewResponses)
);
}
}
render() {
const reviewResponses = this.getResponses();
const reviewResponseNodes = (
<div className="reviewResponse-list">{reviewResponses}</div>
);
return (
<div className="reviewResponse-box">
{reviewResponses.length ? (
<>{reviewResponseNodes}</>
) : (
<ReviewResponseForm addResponse={this.addResponse.bind(this)} />
)}
</div>
);
}
addResponse(review_id, author, body) {
const reviewResponse = {
review_id,
author,
body
};
this.setState({
reviewResponses: this.state.reviewResponses.concat([reviewResponse])
}); // *new array references help React stay fast, so concat works better than push here.
}
getResponses() {
return this.state.reviewResponses.map((reviewResponse) => {
return (
<ReviewResponse
author={reviewResponse.author}
body={reviewResponse.body}
review_id={this.state.review_id}
/>
);
});
}
}

Cannot delete Item from Todo-list in React

I have created a simple Todo list, adding item works but when I clicked on the 'delete' button, my Item is not deleting any item from the List. I would like to know what mistakes I am making in my code, Would appreciate all the help I could get. Thanks in Advance!
And ofcourse, I have tried Looking through google and Youtube, But just couldnot find the answer I am looking for.
Link: https://codesandbox.io/embed/simple-todolist-react-2019oct-edbjf
App.js:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import TodoForm from "./TodoForm";
import Title from "./Title";
class App extends React.Component {
// myRef = React.createRef();
render() {
return (
<div className="App">
<Title />
<TodoForm />
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
----------------------
TodoForm.js:
import React from "react";
import ListItems from "./ListItems";
class TodoForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
items: [],
id: 0
};
}
inputValue = e => {
this.setState({ value: e.target.value });
};
onSubmit = e => {
e.preventDefault();
this.setState({
value: "",
id: 0,
items: [...this.state.items, this.state.value]
});
};
deleteItem = (itemTobeDeleted, index) => {
console.log("itemTobeDeleted:", itemTobeDeleted);
const filteredItem = this.state.items.filter(item => {
return item !== itemTobeDeleted;
});
this.setState({
items: filteredItem
});
};
// remove = () => {
// console.log("removed me");
// };
render() {
// console.log(this.deleteItem);
console.log(this.state.items);
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
placeholder="Enter task"
value={this.state.value}
onChange={this.inputValue}
/>
<button>Add Item</button>
</form>
<ListItems items={this.state.items} delete={() => this.deleteItem} />
</div>
);
}
}
export default TodoForm;
----------------------
ListItems.js
import React from "react";
const ListItems = props => (
<div>
<ul>
{props.items.map((item, index) => {
return (
<li key={index}>
{" "}
{item}
<button onClick={props.delete(item)}>Delete</button>
</li>
);
})}
</ul>
</div>
);
export default ListItems;
The problem is, you must pass a function to the onDelete, but you are directly calling the function
updating the delete item like so,
deleteItem = (itemTobeDeleted, index) => (event) => {
and update this line, (since the itemTobeDeleted was not reaching back to the method)
<ListItems items={this.state.items} delete={(item) => this.deleteItem(item)} />
fixes the issue
Working sandbox : https://codesandbox.io/s/simple-todolist-react-2019oct-zt5w6
Here is the working example: https://codesandbox.io/s/simple-todolist-react-2019oct-xv3b5
You have to pass in the function into ListItems and in ListItems run it passing in the correct argument (the item).
Your solution is close; there are two fixes needed for your app to work as expected.
First, when rendering the ListItems component, ensure that the item is passed through to your deleteItem() function:
<ListItems items={this.state.items} delete={(item) => this.deleteItem(item)} />
Next, your ListItems component needs to be updated so that the delete callback prop is called after an onclick is invoked by a user (rather than immediatly during rendering of that component). This can be fixed by doing the following:
{ props.items.map((item, index) => {
return (<li key={index}>{item}
{/*
onClick is specified via inline callback arrow function, and
current item is passed to the delete callback prop
*/}
<button onClick={() => props.delete(item)}>Delete</button>
</li>);
)}
Here's a working version of your code sandbox
first make a delete function pass it a ind parameter and then use filter method on your array in which you saved the added values like
function delete(ind){
return array.filter((i)=>{
return i!==ind;
})
}
by doing this elements without the key which you tried to delete will not be returned and other elements will be returned.

Displaying Multiple API Responses in React

I am learning React and I have a solution that requests information through an API, and when there is a response it sets the state, however when rendering my results it only shows the last response on screen,
Even though there are 4, see image below.
App.js
import React from 'react';
import Tiles from './components/Tiles'
import Form from './components/Form'
import WaterData from './components/WaterData'
class App extends React.Component{
state = {
station_name: undefined,
water_value: undefined,
dateTime: undefined
}
getData = async (e) => {
e.preventDefault();
const name = e.target.elements.name.value;
const api_call = await fetch(`https://waterlevel.ie/geojson/latest/`)
.then(response1 => {
response1.json().then(data =>{
Array.from(data.features).forEach(element => {
if(element.properties['station.name'] === name){
this.setState({
station_name: element.properties['station.name'],
water_value: element.properties['value'],
dateTime: element.properties['datetime'],
});
}
})
});
});
}
render(){
return(
<div>
<Tiles />
<Form loadData={this.getData}/>
<WaterData
station_name={this.state.station_name}
water_value={this.state.water_value}
dateTime={this.state.dateTime}
/>
</div>
)
}
}
export default App;
WaterData.js
import React from 'react';
const Weather = (props) => {
console.log(props)
return(
<li>
<p>Location {props.station_name}</p>
<p>Value {props.water_value}</p>
<p>Date Time: {props.dateTime}</p>
</li>
)
}
export default Weather;
Can someone explain to me why the 4 responses do not display?
This happens because you are replacing the values in your state for each part of your data.
You can filter out the element you want in your array using filter.
And then put the whole array into your state only once :
const api_call = await fetch(`https://waterlevel.ie/geojson/latest/`)
.then(response1 => {
response1.json().then(data => {
const features = Array.from(data.features)
.filter(el => el.properties['station.name'] === name);
this.setState({ features });
})
});
But now, to render all of them, you will need to map your state values :
render(){
return(
<div>
<Tiles />
<Form loadData={this.getData}/>
{this.state.features.map(feat => <WaterData
key={/* Find something unique*/}
station_name={feat.properties['station.name']}
water_value={feat.properties['value']}
dateTime={feat.properties['datetime']}
/>)}
</div>
)
}
There's no need to store all the value separately in your state if they are related to each other, it would be fine for your child component though.
To be sure that the state value is always an array, give it an empty array at the start of your class :
state = {
features: []
}

Re-render the component with new data without having local state in React

I'm practicing react and redux and I'm creating a simple app where I have a sidebar showing a list of categories that is visible on every route and the main area that initially displays all the books I have and when clicking on a category link on the sidebar the main area loading another component with all the books related to this category.
Here's my routes setup in the App.js file ...
class App extends Component {
async componentDidMount() {
try {
await this.props.asyncLoadBooks();
await this.props.asyncLoadCategories();
} catch (error) {
console.log(error);
}
}
render() {
return (
<>
<Header />
<div className="global-wrapper">
<div className="container">
<aside className="side-bar">
<Categories />
</aside>
<main className="main-content">
<Switch>
<Route exact path="/" component={Books} />
<Route
exact
path="/category/:id"
component={Category}
/>
<Route component={NotFound} />
</Switch>
</main>
</div>
</div>
</>
);
}
}
In the App.js as you can see I'm loading the data via a local JSON file with axios in the Actions files of the booksActions and categoriesAction, it's pretty straightforward.
And here's the Categories component ...
class Categories extends Component {
render() {
const { categories } = this.props;
let categoriesList;
if (categories && categories.length !== 0) {
categoriesList = categories.map(category => (
<li key={category.id}>
<Link to={`/category/${category.id}`}>{category.name}</Link>
</li>
));
} else {
categoriesList = <Loading />;
}
return (
<div>
<h2>Categories</h2>
<ul>{categoriesList}</ul>
</div>
);
}
}
const mapState = state => ({
categories: state.categories.categories
});
export default connect(mapState)(Categories);
And I'm firing another action in the ComponentDidMount() of the single Category component to get all the books related to that component and render them ...
class Category extends Component {
componentDidMount() {
this.props.getCategoryBooks(this.props.match.params.id);
}
componentDidUpdate(prevProps) {
if (prevProps.match.params.id !== this.props.match.params.id) {
this.props.getCategoryBooks(this.props.match.params.id);
}
}
render() {
const { categoryBooks } = this.props;
return (
<div>
{/* <h1>{this.props.match.params.id}</h1> */}
{categoryBooks &&
categoryBooks.map(book => {
return <div key={book.id}>{book.title}</div>;
})}
</div>
);
}
}
const mapState = state => ({
categories: state.categories.categories,
categoryBooks: state.books.categoryBooks
});
const mapActions = {
getCategoryBooks
};
export default connect(
mapState,
mapActions
)(Category);
Now, everything is working the first time, however, when I click on another category the <Category /> component doesn't get updated because I'm dispatching the action in the componentDidMount() thus the component already mounted the first time, so it doesn't dispatch the action again after I click on another category, now what is the best way to handle this?
The second issue is where I'm on a category route http://localhost:3000/category/9967c77a-1da5-4d69-b6a9-014ca20abd61 and I try to refresh the page, the categoris list loads fine on the sidebar, but the single component shows empty, and when I look on the redux-devtools I find that the GET_CATEGORY_BOOKS action gets fired before the LOAD_BOOKS and LOAD_CATEGORIES in the App.js file, because the child componentDidMount() method gets called before its parent equivalent method. How to solve this as well?
I hope you guys can help me in this.
Edit
As ##NguyễnThanhTú noticed, the componentDidupate had a typo, now it works when clicking on another category.
That leaves us with the second issue when reloading the page in the category route and the data doesn't show because of the App.js componentDidMount fires after its children components.
Edit
Here's a repo on Github for this project ...
https://github.com/Shaker-Hamdi/books-app
In your booksActions.js, add this:
export const getCategoryBooksV2 = categoryId => {
return async (dispatch, getState) => {
const { books } = getState();
if (books.books.length === 0) {
console.log('Only executing once') // testing purpose only
const response = await axios.get("books.json");
const data = response.data.books;
dispatch(loadBooks(data));
dispatch(getCategoryBooks(categoryId));
}
dispatch(getCategoryBooks(categoryId));
};
};
In your Category.js, use that new action creator:
import { getCategoryBooksV2 } from "../books/booksActions";
...
componentDidMount() {
this.props.getCategoryBooksV2(this.props.match.params.id);
}
...
const mapActions = {
getCategoryBooksV2
};
This solution is inspired by this example:
function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
From the Redux-Thunk Documentation
This is the demo:

Categories

Resources