Components not updating when state changes - reactjs

I'm making a venue review app with react/redux toolkit/firebase.
The user can submit, edit, and delete reviews for a particular venue. The problem I have is that CRUD operations for reviews are not instantly shown in the component - I have to refresh the page to see the changes.
I suspect this it's because how I've structured my firestore database, and/or with how I'm handling state in the front end.
My firebase db is structured as follows:
There's one collection "venues", with documents that have the following shape:
name:'',
photo:'',
reviews: [
{
title:'',
blurb:'',
reviewId:''
},
{...}
]
In my front end, I'm fetching all the "venues" documents in venueSlice.js :
import { createSlice,createAsyncThunk } from "#reduxjs/toolkit";
import { collection,query,getDocs,doc,updateDoc,arrayUnion, arrayRemove, FieldValue } from "firebase/firestore";
import { db } from "../../firebaseConfig";
const initialState = {
venues: [],
isLoading: true
}
export const fetchVenues = createAsyncThunk("venues/fetchVenues", async () => {
try {
const venueArray = [];
const q = query(collection(db, "venues"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) =>
venueArray.push({ id: doc.id, ...doc.data() })
);
return venueArray;
} catch (err) {
console.log("Error: ", err);
}
});
...
const venueSlice = createSlice({
name: "venues",
initialState,
reducers: {},
extraReducers(builder) {
builder
.addCase(fetchVenues.fulfilled, (state, action) => {
state.isLoading = false
state.venues = action.payload;
})
.addCase(fetchVenues.pending, (state) => {
state.isLoading = true
})
.addCase(fetchVenues.rejected, (state) => {
state.isLoading = false
})
},
});
As as example of CRUD operation, I'm deleting reviews in Reviews.js
import { useDispatch } from "react-redux";
import { deleteReview,fetchVenues } from "../features/venues/venueSlice";
import { Link } from "react-router-dom";
const Reviews = ({ venue }) => {
const dispatch = useDispatch()
const venueId = venue[0]?.id
const removeReview = (review) => {
dispatch(deleteReview({...review, id:venueId}))
}
const content = venue[0]?.reviews.map(review => (
<div className="review" key = {review.reviewId}>
<h2>{review.title}</h2>
<h3>{review.blurb}</h3>
<div>
<Link to = {`/venue/${venue[0].id}/${review.reviewId}/edit`}><button>Edit</button></Link>
<button onClick = {() => removeReview(review)}>Delete</button>
</div>
</div>
))
return (
<div className="all-reviews">
{content}
</div>
)
}
export default Reviews;
....which is handled by a thunk in venueSlice.js
export const deleteReview = createAsyncThunk("venues/deleteReview", async (review) => {
const newReview = {blurb:review.blurb, title: review.title, reviewId: review.reviewId}
try {
const venueRef = doc(db,"venues",review.id)
await updateDoc(venueRef, {
reviews: arrayRemove(newReview)
})
} catch (err) {
console.log('Error: ', err)
}
})
Lastly, venues are fetched in App.js
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { useEffect } from "react";
import { useSelector,useDispatch } from "react-redux";
import { fetchVenues,fetchReviews } from "./features/venues/venueSlice";
import Venue from "./features/venues/Venue";
import VenueList from "./features/venues/VenueList";
import AddReview from "./components/AddReview";
import EditReview from "./components/EditReview";
import './styles.css'
const App = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(fetchVenues())
},[])
return (
<Router>
<Routes>
<Route path = '/' element = {<VenueList/>}/>
<Route path = '/add-review' element = {<AddReview/>}/>
<Route path = '/venue/:id' element = {<Venue/>}/>
<Route path = '/venue/:id/:reviewId/edit' element = {<EditReview/>}/>
</Routes>
</Router>
);
}
export default App;
I was thinking the problem is this: the venues collection is fetched and set to global state, and my application will respond to changes in venue state - but not changes of state within the reviews nested array. A possible solution would be to make a new "reviews" collection, or make a reviews subcollection in every venue document.
Suggestions?

Venues are not fetched in App.js after you delete a review. useEffect's dependency array is empty, which means useEffect's effect (first argument's body) only gets executed when the component mounts.
One possible solution is fetching venues right after successfully deleting a review.

To get realtime updates, you can use onSnapshot (docs) instead of getDocs

Related

Page loads on first render, then crashes entire app when refreshed

I'm building a simple venue review app using react/redux/firebase.
On the homepage are a list of venues; when you click on a specific venue it routes the user to a page specifically about the venue (Venue.js).
Problem: Venue.js renders correctly upon first render, but crashes the entire app when I refresh the page. It also only renders when the user clicks on link to the specific venue - if I try to reach the page by entering the venue page URL, it crashes.
I've tried using optional chaining in Venue.js (as follows) but it hasn't worked
changed
const venue = venues.venues.filter((item) => item.id === id);
to
const venue = venues?.venues?.filter((item) => item.id === id);
Any ideas on how to fix this? I figured it's an async problem, or maybe I'm using useEffect incorrectly.
venueSlice.js : Manages state for venues and API calls to firebase
import { createSlice,createAsyncThunk } from "#reduxjs/toolkit";
import { collection,query,getDocs,doc,updateDoc,arrayUnion } from "firebase/firestore";
import { db } from "../../firebaseConfig";
const initialState = {
venues: []
}
export const fetchVenues = createAsyncThunk("venues/fetchVenues", async () => {
try {
const venueArray = [];
const q = query(collection(db, "venues"));
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) =>
venueArray.push({ id: doc.id, ...doc.data() })
);
return venueArray;
} catch (err) {
console.log("Error: ", err);
}
});
export const postReview = createAsyncThunk("venues/postReview", async (review) => {
try {
const venueRef = doc(db,"venues",review.id)
await updateDoc(venueRef, {
reviews: arrayUnion({
title:review.title,
blurb:review.blurb,
reviewId:review.reviewId })
})
} catch (err) {
console.log('Error :', err)
}
})
const venueSlice = createSlice({
name: "venues",
initialState,
reducers: {},
extraReducers(builder) {
builder
.addCase(fetchVenues.fulfilled, (state, action) => {
state.venues = action.payload;
})
},
});
export default venueSlice.reducer
VenueList.js : where all venues are listed
import { Link } from "react-router-dom";
import { useEffect } from "react";
import { fetchVenues } from "./venueSlice";
import { useSelector,useDispatch } from "react-redux";
const VenueList = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchVenues());
}, [dispatch]);
const venues = useSelector((state) => state.venues);
const content = venues.venues.map((venue) => (
<Link to={`/venue/${venue.id}`} style = {{textDecoration: "none"}}>
<div className="venue-item">
<h2>{venue.name}</h2>
<img src={venue.photo} />
</div>
</Link>
));
return (
<div className="venue-list">
{content}
</div>
);
};
export default VenueList;
Venue.js : where specific venue information is provided
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import AddReview from "../../components/AddReview";
import Reviews from "../../components/Reviews";
const Venue = () => {
const { id } = useParams();
const venues = useSelector((state) => state.venues);
const venue = venues.venues.filter((item) => item.id === id);
const content = venue.map((item) => (
<div className="venue-page-main">
<h2>{item.name}</h2>
<img src={item.photo} alt = "venue"/>
</div>
));
return (
<>
{content}
<AddReview id = {id}/>
<Reviews venue = {venue}/>
</>
);
};
export default Venue;
App.js
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Venue from "./features/venues/Venue";
import VenueList from "./features/venues/VenueList";
import AddReview from "./components/AddReview";
import './styles.css'
const App = () => {
return (
<Router>
<Routes>
<Route path = '/' element = {<VenueList/>}/>
<Route path = '/add-review' element = {<AddReview/>}/>
<Route path = '/venue/:id' element = {<Venue/>}/>
</Routes>
</Router>
);
}
export default App;

Re-render after delete item with redux thunk

I have a problem updating and displaying data from the api.
The problem occurs when I delete a specific item, sometimes the change is displayed (rendered) and other times not, until after the refresh.
Where can there be a mistake please?
Component
import React, { useEffect } from "react";
import { useParams } from "react-router-dom";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { getTodoItems, deleteTodoItem } from "../redux/todoSlice";
const TodoList = () => {
const { id } = useParams();
const dispatch = useDispatch();
const { todoList, urlParams } = useSelector((state) => state.todo);
useEffect(() => {
dispatch(getTodoItems(id));
dispatch(setUrlParams(+id));
}, [dispatch, id]);
const handleClick = (e) => {
dispatch(deleteTodoItem(urlParams, e.target.id));
dispatch(getTodoItems(urlParams));
};
return (
<div>
{todoList.map((todo, id) => {
<div key={id}>
<p>{todo.text}</p>
<button id={todo.id} onClick={handleClick}>
Delete
</button>
</div>;
})}
</div>
);
};
export default TodoList;
Slice
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
todoList: null,
urlParams: null,
};
export const todoSlice = createSlice({
name: "todo",
initialState,
reducers: {
setItems: (state, action) => {
state.todoList = action.payload;
},
setUrlParams: (state, action) => {
state.urlParams = action.payload;
},
},
});
export const { setItems, setUrlParams } = todoSlice.actions;
export default todoSlice.reducer;
// Load todo items
export const getTodoItems = (id) => {
return (dispatch) => {
axios
.get(`https://xxx.mockapi.io/api/list/${id}/todolist`)
.then((resp) => dispatch(setItems(resp.data)));
};
};
// Delete todo item
export const deleteTodoItem = (idList, idTodo) => {
return (dispatch) => {
axios
.delete(`https://xxx.mockapi.io/api/list/${idList}/todolist/${idTodo}`)
.then(
axios
.get(`https://xxx.mockapi.io/api/list/${idList}/todolist`)
.then((resp) => dispatch(setItems(resp.data)))
);
};
};
Thus, the first comes to download data from api -> render this data -> delete a specific item -> re-render the list without the deleted item
The problem occurs in the re-rendering, sometimes it happens that the data is erased in the background but the change is not apparent, but sometimes absolutely nothing happens, and the change occurs only after clicking the delete button again.

Map fetched data in React with Redux Toolkit

i´m trying to fetch all the product i have in my DB (mongodb) using the fetch API. The result i get i store in a slice using Redux Toolkit. The problem is when i pass the data fetched and stored to a component as a props, the result is not beeing displayed.
slice
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
products: [],
};
const productSlice = createSlice({
name: "product",
initialState,
reducers: {
setProducts(state, action) {
state.products.push(action.payload);
},
},
});
export const { setProducts } = productSlice.actions;
export default productSlice.reducer;
store
import { configureStore } from "#reduxjs/toolkit";
import uiSlice from "./ui-slice";
import userSlice from "./user-slice";
import productSlice from './product-slice';
const store = configureStore({
reducer: {
ui: uiSlice,
user: userSlice,
products: productSlice
},
});
export default store;
function i used to fetch the products
export const fetchProduct = () => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:xx/xxx");
const data = await response.json();
let loadedProducts = [];
for (const key in data) {
loadedProducts.push({
id: data[key]._id,
productName: data[key].productName,
price: data[key].price,
imageUrl: data[key].imageUrl,
});
}
dispatch(setProducts(loadedProducts));
} catch (error) {
console.log(error);
}
};
I get the value stored in my redux state with useSelector and use it to fetch the products
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Container } from "react-bootstrap";
import { fetchProduct } from "../../actions/productActions";
import Hero from "../hero/Hero";
import Footer from "../footer/Footer";
import DisplayProductsList from "../displayProduct/DisplayProductsList";
export default function Home() {
const productsInfo = useSelector((state) => state.products.products);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProduct());
}, []);
return (
<>
<Hero />
<DisplayProductsList products={productsInfo} />
<Container fluid>
<Footer></Footer>
</Container>
</>
);
}
And then i map it
export default function DisplayProductsList(props) {
console.log(props)
return (
<ul>
{props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))}
</ul>
);
}
console log the props i sent
if i console log the state in the selector this is what i get
code in the backend
module.exports.fetchProduct = async (req, res) => {
try {
const products = await Product.find({});
console.log(products)
if (products) {
res.json(products);
}
} catch (error) {
console.log(error);
}
};
As I can see in your code, you are storing an array of objects i.e. 'loadedProducts' into 'products' which is also an array. Something like this:
nested array
So in order to get the productsInfo in DisplayProductsList component, instead of doing this:
{props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))}
you should do something like this:
{props.products[0].map((product) => (
//rest of your code
))}

Redux vs Local State vs React Query

I just can't decide the pattern I want to follow.
I'm implementing what I call a UserParent component. Basically a list of users and when you click on a user, it loads their resources.
Approach 1: Redux
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { NavList } from '../nav/NavList'
import { ResourceList } from '../resource/ResourceList'
import { getUserResources, clearResources } from './userSlice'
import CircularProgress from '#mui/material/CircularProgress';
import { getAllUsers } from './userSlice';
export const UserParent = () => {
const users = useSelector((state) => state.users.users )
const resources = useSelector((state) => state.users.user.resources )
const [highLightedUsers, setHighLightedItems] = useState([]);
const isLoading = useSelector((state) => state.users.isLoading)
let dispatch = useDispatch();
useEffect(() => {
dispatch(getAllUsers());
}, [])
const onUserClick = (user) => {
if (highLightedUsers.includes(user.label)) {
setHighLightedItems([])
dispatch(clearResources())
} else {
setHighLightedItems([...highLightedUsers, user.label])
dispatch(getUserResources(user.id))
}
}
return(
<>
{ isLoading === undefined || isLoading ? <CircularProgress className="search-loader" /> :
<div className="search-container">
<div className="search-nav">
<NavList
items={users}
onItemClick={onUserClick}
highLightedItems={highLightedUsers}
/>
</div>
<div className="search-results">
<ResourceList resources={resources} />
</div>
</div> }
</>
)
}
And then we have the reducer code:
import { createSlice } from '#reduxjs/toolkit';
import Api from '../../services/api';
const INITIAL_STATE = {
users: [],
isLoading: true,
user: { resources: [] }
};
export const userSlice = createSlice({
name: 'users',
initialState: INITIAL_STATE,
reducers: {
loadAllUsers: (state, action) => ({
...state,
users: action.payload,
isLoading: false
}),
toggleUserLoader: (state, action) => ({
...state,
isLoading: action.payload
}),
loadUserResources: (state, action) => ({
...state, user: { resources: action.payload }
}),
clearResources: (state) => ({
...state,
isLoading: false,
user: { resources: [] }
})
}
});
export const {
loadAllUsers,
toggleUserLoader,
loadUserResources,
clearResources
} = userSlice.actions;
export const getAllUsers = () => async (dispatch) => {
try {
const res = await Api.fetchAllUsers()
if (!res.errors) {
dispatch(loadAllUsers(res.map(user => ({id: user.id, label: user.full_name}))));
} else {
throw res.errors
}
} catch (err) {
alert(JSON.stringify(err))
}
}
export const getUserResources = (userId) => async (dispatch) => {
try {
const res = await Api.fetchUserResources(userId)
if (!res.errors) {
dispatch(loadUserResources(res));
} else {
throw res.errors
}
} catch (err) {
alert(JSON.stringify(err))
}
}
export default userSlice.reducer;
This is fine but I am following this pattern on every page in my app. While it is easy follow I don't believe I'm using global state properly. Every page makes and API call and loads the response into redux, not necessarily because it needs to be shared (although it may be at some point) but because it's the pattern I'm following.
Approach 2: Local State
import React, { useEffect, useState } from 'react'
import { NavList } from '../nav/NavList'
import { ResourceList } from '../resource/ResourceList'
import CircularProgress from '#mui/material/CircularProgress';
import Api from '../../services/api';
export const UserParent = () => {
const [users, setUsers] = useState([])
const [resources, setResources] = useState([])
const [highLightedUsers, setHighLightedItems] = useState([]);
const [isLoading, setIsLoading] = useState(true)
const getUsers = async () => {
try {
const res = await Api.fetchAllUsers()
setUsers(res.map(user => ({id: user.id, label: user.full_name})))
setIsLoading(false)
} catch (error) {
console.log(error)
}
}
const getUserResources = async (userId) => {
try {
setIsLoading(true)
const res = await Api.fetchUserResources(userId)
setResources(res)
setIsLoading(false)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
getUsers()
}, [])
const onUserClick = (user) => {
if (highLightedUsers.includes(user.label)) {
setHighLightedItems([])
} else {
setHighLightedItems([...highLightedUsers, user.label])
getUserResources(user.id)
}
}
return(
<>
{ isLoading === undefined || isLoading ? <CircularProgress className="search-loader" /> :
<div className="search-container">
<div className="search-nav">
<NavList
items={users}
onItemClick={onUserClick}
highLightedItems={highLightedUsers}
/>
</div>
<div className="search-results">
<ResourceList resources={resources} />
</div>
</div>}
</>
)
}
What I like about this is that it uses local state and doesn't bloat global state however, I don't like that it still has business logic in the component, I could just move these to a different file but first I wanted to try React Query instead.
Approach 3: React Query
import React, { useState } from 'react'
import { NavList } from '../nav/NavList'
import { ResourceList } from '../resource/ResourceList'
import CircularProgress from '#mui/material/CircularProgress';
import Api from '../../services/api';
import { useQuery } from "react-query";
export const UserParent = () => {
const [resources, setResources] = useState([])
const [highLightedUsers, setHighLightedItems] = useState([]);
const getUsers = async () => {
try {
const res = await Api.fetchAllUsers()
return res
} catch (error) {
console.log(error)
}
}
const { data, status } = useQuery("users", getUsers);
const getUserResources = async (userId) => {
try {
const res = await Api.fetchUserResources(userId)
setResources(res)
} catch (error) {
console.log(error)
}
}
const onUserClick = (user) => {
if (highLightedUsers.includes(user.label)) {
setHighLightedItems([])
} else {
setHighLightedItems([...highLightedUsers, user.label])
getUserResources(user.id)
}
}
return(
<>
{ status === 'loading' && <CircularProgress className="search-loader" /> }
<div className="search-container">
<div className="search-nav">
<NavList
items={data.map(user => ({id: user.id, label: user.full_name}))}
onItemClick={onUserClick}
highLightedItems={highLightedUsers}
/>
</div>
<div className="search-results">
<ResourceList resources={resources} />
</div>
</div>
</>
)
}
This is great but there is still business logic in my component, so I can move those functions to a separate file and import them and then I end up with this:
import React, { useState } from 'react'
import { UserList } from '../users/UserList'
import { ResourceList } from '../resource/ResourceList'
import CircularProgress from '#mui/material/CircularProgress';
import { getUsers, getUserResources } from './users'
import { useQuery } from "react-query";
export const UserParent = () => {
const [resources, setResources] = useState([])
const [highLightedUsers, setHighLightedItems] = useState([]);
const { data, status } = useQuery("users", getUsers);
const onUserClick = async (user) => {
if (highLightedUsers.includes(user.full_name)) {
setHighLightedItems([])
} else {
setHighLightedItems([...highLightedUsers, user.full_name])
const res = await getUserResources(user.id)
setResources(res)
}
}
return(
<>
{ status === 'loading' && <CircularProgress className="search-loader" /> }
<div className="search-container">
<div className="search-nav">
<UserList
users={data}
onUserClick={onUserClick}
highLightedUsers={highLightedUsers}
/>
</div>
<div className="search-results">
<ResourceList resources={resources} />
</div>
</div>
</>
)
}
In my opinion this is so clean! However, is there anything wrong with the first approach using Redux? Which approach do you prefer?
The first approach you are using shows a very outdated style of Redux.
Modern Redux is written using the official Redux Toolkit (which is the recommendation for all your production code since 2019. It does not use switch..case reducers, ACTION_TYPES, immutable reducer logic, createStore or connect. Generally, it is about 1/4 of the code.
What RTK also does is ship with RTK-Query, which is similar to React Query, but even a bit more declarative than React Query. Which one of those two you like better is probably left to personal taste.
I'd suggest that if you have any use for Redux beyond "just api fetching" (which is a solved problem given either RTK Query or React Query), you can go with Redux/RTK-Query.
If you don't have any global state left after handling api caching, you should probably just go with React Query.
As for learning modern Redux including RTK Query, please follow the official Redux tutorial.
Personally I prefer React-Query for all API-calls, it is great it useMutate and how it manages re-fetching, invalidating queries and more.
I am using your third approach where I create the queries in separate files and then import them where needed.
So far it has been great, and I am using RecoilJS for managing global states. And with the right approach there is really not much that actually needs to be in a global state IMO. Some basic auth/user info and perhaps notification management. But other than that I have been putting less and less in global states keeping it much simpler and scoped.

React UseState hook causing infinite loop

I am using ReactJs to grab an RSS news feed every 5 seconds to convert it into a JSON string to render it on the webpage. I am using both useEffect and useState hook for this purpose as I am passing the JSON string in the useState hook variable, however. It kind of works but it produces an infinite loop. I have searched through the fixes provided in stack overflow but I couldn't find the exact problem. Here is my code snippet.'
import React, {useEffect, useState} from 'react';
import Carousel from 'react-bootstrap/Carousel';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {getNews} from "../../actions/news";
import Parser from 'rss-parser';
const NewsCarousel = ({getNews, news: {news, loading} }) => {
const [getFeed, setFeed] = useState({
feed: ''
});
useEffect(() => {
const interval = setInterval(() => {
getNews();
}, 5000);
return () => clearInterval(interval);
}, [getNews]);
const { feed } = getFeed;
const newsFeed = feed => setFeed({ ...getFeed, feed: feed });
let parser = new Parser();
parser.parseString(news, function(err, feed){
if (!err) {
newsFeed(feed);
} else {
console.log(err);
}
});
console.log(feed);
return (
<div className="dark-overlay">
</div>
);
};
NewsCarousel.propTypes = {
getNews: PropTypes.func.isRequired,
news: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
news: state.news
});
export default connect(mapStateToProps, {getNews}) (NewsCarousel);
Its when I console.log my feed variable that's when I see in the console the infinite logs.
Below is my getNews Action
import axios from 'axios';
import { GET_NEWS, NEWS_FAIL } from "./types";
export const getNews = () => async dispatch => {
try{
const res = await axios.get('https://www.cbc.ca/cmlink/rss-
topstories');
dispatch({
type: GET_NEWS,
payload: res.data
})
} catch(err) {
dispatch({
type: NEWS_FAIL,
payload: { msg: err}
})
}
};
You need to parse your news only when there is a change in new props. Add another useEffect with news as a dependency so it will be called when the news changes and then update your state there.
import React, {useEffect, useState} from 'react';
import Carousel from 'react-bootstrap/Carousel';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import {getNews} from "../../actions/news";
import Parser from 'rss-parser';
const NewsCarousel = ({getNews, news: {news, loading} }) => {
const [getFeed, setFeed] = useState({
feed: ''
});
useEffect(() => {
const interval = setInterval(() => {
getNews();
}, 5000);
return () => clearInterval(interval);
}, [getNews]);
useEffect(() => {
const newsFeed = feed => setFeed({ ...getFeed, feed: feed });
const parser = new Parser();
parser.parseString(news, function(err, feed){
if (!err) {
newsFeed(feed);
} else {
console.log(err);
}
});
}, [news]);
return (
<div className="dark-overlay">
</div>
);
};
NewsCarousel.propTypes = {
getNews: PropTypes.func.isRequired,
news: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
news: state.news
});
export default connect(mapStateToProps, {getNews}) (NewsCarousel);

Resources