React redux not fetching data from API - reactjs

Hi im new to redux and im trying to create a movie app using the API from www.themoviedb.org. I am trying to display the popular movies and im sure the API link works since ive tested it in postman but i cant seem to figure out why redux doesnt pick up the data.
//action
import { FETCH_POPULAR } from "./types";
import axios from "axios";
export const fetchPopularMovies = () => (dispatch) => {
axios
.get(
`https://api.themoviedb.org/3/movie/popular?api_key=${API}&language=en-US`
)
.then((response) =>
dispatch({
type: FETCH_POPULAR,
payload: response.data
})
)
.catch((err) => console.log(err));
};
//reducer
import { FETCH_POPULAR } from "../actions/types";
const initialState = {
popular: [],
};
export default function (state = initialState, action) {
switch (action.type) {
case FETCH_POPULAR:
return {
...state,
popular: action.payload,
};
default:
return state;
}
}
import React from "react";
import { connect } from "react-redux";
import Popular from "./Popular";
const FetchedPopular = (props) => {
const { popular } = props;
let content = "";
content =
popular.length > 0
? popular.map((item, index) => (
<Popular key={index} popular={item} />
))
: null;
return <div className="fetched-movies">{content}</div>;
};
const mapStateToProps = (state) => ({
popular: state.popular.popular,
});
export default connect(mapStateToProps)(FetchedPopular);
import React from "react";
import "../Styles.css";
const Popular = (props) => {
return (
<div className="movie-container">
<img
className="poster"
src={`https://image.tmdb.org/t/p/w400/${props.poster_path}`}
/>
</div>
);
};
export default Popular;
I cant really tell what I'm missing can someone help?

Next to mapStateToProps you need to create mapDispatchToProps. After that, you will be able to call your Redux action from your React component.
I suggest you the mapDispatchToProps as an Object form. Then you need to use this mapDispatchToProps as the second parameter of your connect method.
When you will have your action mapped to your component, you need to call it somewhere. It is recommended to do it for example on a component mount. As your React components are Functional components, you need to do it in React useEffect hook.
import React, { useEffect } from "react";
import { connect } from "react-redux";
import Popular from "./Popular";
import { fetchPopularMovies } from 'path_to_your_actions_file'
const FetchedPopular = (props) => {
const { popular } = props;
let content = "";
useEffect(()=> {
// call your mapped action (here it is called once on component mount due the empty dependency array of useEffect hook)
props.fetchPopularMovies();
}, [])
content =
popular.length > 0
? popular.map((item, index) => (
<Popular key={index} popular={item} />
))
: null;
return <div className="fetched-movies">{content}</div>;
};
const mapStateToProps = (state) => ({
popular: state.popular.popular,
});
// create mapDispatchToProps
const mapDispatchToProps = {
fetchPopularMovies
}
// use mapDispatchToProps as the second parameter of your `connect` method.
export default connect(mapStateToProps, mapDispatchToProps)(FetchedPopular);
Moreover, as I wrote above in my comment, your Popular does not have the prop poster_path but it has the prop popular which probably has the property poster_path.
import React from "react";
import "../Styles.css";
const Popular = (props) => {
return (
<div className="movie-container">
<img
className="poster"
src={`https://image.tmdb.org/t/p/w400/${props.popular.poster_path}`}
/>
</div>
);
};
export default Popular;

Related

Function doesn't enter in the dispatch in React / Redux

I'm working on a personal React project with Redux and I've an issue. All my redux functions are working well except one. I want to load all the requests into the redux. In requestAction.js, I have the first console.log but not the second from the return function(dispatch). Have you any idea why ?
Thank you in advance =)
Here is my code :
import React, {useState, useEffect} from "react"
import {connect} from "react-redux"
import {getAllRequests} from "../../api/requests"
import {loadAllRequests} from "../../actions/request/requestAction"
import {convertDate} from "../../utils/utils"
import Header from "../headers/header"
import HeaderPages from "../headers/headerPages"
import Footer from "../footer"
import AdminMenu from "../../components/adminMenu"
const AdminRequests = (props) => {
const headerTitle ="Administration"
const headerBreadcrumbs = [{value: "Accueil", link:"/"},{value: "Administration", link:"/admin"},{value: "Commandes", link:null}]
const [displayedRequests, setDisplayedRequests] = useState([])
useEffect(() => {
loadDatas()
}, [])
useEffect(() => {
loadDisplayedRequests(props.requests.list)
}, [props.requests])
const loadDatas = () => {
getAllRequests()
.then(requestsDB => {
loadAllRequests(requestsDB) //My redux function
})
}
//Construction de la liste des commandes à afficher
const loadDisplayedRequests = (requests) => {
requests.map((requestItem) => {
setDisplayedRequests(displayedRequests => [...displayedRequests,
<article key={requestItem.id} className="profile-user-request-item">
<section className="request-item-header">
<p>N°{requestItem.request_number}</p>
<p>du {convertDate(requestItem.request_date)}</p>
<p>Statut : {requestItem.preparation_status}</p>
</section>
<section className="request-item-resume">
<p>Total</p>
<p>{requestItem.total_amount} € TCC</p>
</section>
</article>])
})
}
const showDisplayedRequests = () => {
return(
<section>
{displayedRequests}
</section>
)
}
return (
<div className="root">
<Header />
<HeaderPages headerTitle={headerTitle} headerBreadcrumbs={headerBreadcrumbs}/>
<section className="admin-container">
<AdminMenu />
<section className="admin-content">
<h4>Gestion des commandes</h4>
{showDisplayedRequests()}
</section>
</section>
<Footer />
</div>
)
}
const mapStateToProps = (store) => {
return {
requests: store.requests
}
}
const mapDispatchToProps = {
loadAllRequests
}
export default connect(mapStateToProps, mapDispatchToProps)(AdminRequests)
requestAction.js
import {LOAD_ALL_REQUESTS} from "./action-type"
export const loadAllRequests = (requests) => {
console.log("requests action = ", requests) //Displayed
return function(dispatch){
console.log("dispatch") //Not displayed
dispatch({
type: LOAD_ALL_REQUESTS,
payload: requests
})
}
requestReducer.js
import { LOAD_ALL_REQUESTS } from "../actions/request/action-type"
const initialState = {
list: []
}
export default function RequestReducer(state = initialState, action) {
switch(action.type){
case LOAD_ALL_REQUESTS :
return {list: action.payload}
break
default :
return state
break
}
}
index.js
import {combineReducers } from "redux"
import UserReducer from "./userReducer"
import ProductsReducer from "./productsReducer"
import RequestReducer from "./requestReducer"
const rootReducer = combineReducers({
user: UserReducer,
products: ProductsReducer,
requests: RequestReducer
})
export default rootReducer
The problem at heart is that you don't dispatch. You need to
const dispatch = useDispatch()
useEffect(() => {
dispatch(loadDisplayedRequests(props.requests.list))
// instead of
// loadDisplayedRequests(props.requests.list)
}, [props.requests])
Adding to that: What you have written there is not an action creator, but a thunk. It will only work if you have the thunk middleware enabled - and even then, for this simple use case it just does nothing extra that you need.
As a normal action creator, it would look like this:
import {LOAD_ALL_REQUESTS} from "./action-type"
export const loadAllRequests = (requests) => {
return {
type: LOAD_ALL_REQUESTS,
payload: requests
}
}
Generally, I want to make you aware that you are writing a very old style of Redux here and might have been following an outdated tutorial. Modern Redux does not have string action type constants, switch case reducers, action creators or connect any more.
For a quick look at modern Redux, take a look at https://redux.js.org/tutorials/fundamentals/part-8-modern-redux and for a longer tutorial, read https://redux.js.org/tutorials/essentials/part-1-overview-concepts

Pass text value to another component

How to pass text value to another component using Redux in React?
I am learning Redux in React. I am trying to pass text value to another component using Redux in React.
My code is like below
Mycomponent.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Mycomponent extends Component {
state = {
textInput: '',
}
handleChange = event => {
this.props.dispatch({ type: "add" });
}
render = () => {
return (
<div>
<input
type="text"
onChange={this.handleChange} />
</div>
);
}
}
const mapStateToProps = state => ({ nameState: state.nameState});
export default connect(mapStateToProps)(Mycomponent);
nameAction.js
export const nameAction = () => ({
type: 'add'
});
export default { nameAction };
nameReducer.js
const nameReducer = (state = {}, action) => {
switch (action.type) {
case 'add': {
return {
...state,
nameState: action.payload
};
}
default:
return state;
}
};
export default nameReducer;
Outputcomponent.js
import React, { Component } from 'react';
class Outputcomponent extends Component {
render = (props) => {
return (
<div>
<div>{this.props.nameState }</div>
</div>
);
}
}
export default Outputcomponent;
The use of redux hooks explained by Josiah is for me the best approach but you can also use mapDispatchToProps.
Even if the main problem is that you don't pass any data in your 'add' action.
nameAction.js
You call the action.payload in nameReducer.js but it does not appear in your action
export const nameAction = (text) => ({
type: 'add',
payload: text
});
Mycomponent.js
Then as for your state we can mapDispatchToProps.
(I think it's better to trigger the action with a submit button and save the input change in your textInput state, but I guess it's intentional that there is none)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {nameAction} from './nameAction'
class Mycomponent extends Component {
state = {
textInput: '',
}
handleChange = event => {
this.props.nameAction(event.target.value);
}
render = () => {
return (
<div>
<input
type="text"
onChange={this.handleChange} />
</div>
);
}
}
const mapStateToProps = state => ({ nameState: state.nameState});
const mapDispatchToProps = dispatch => ({ nameAction: (text) => dispatch(nameAction(text))});
export default connect(mapStateToProps,mapDispatchToProps)(Mycomponent);
OutputComponent.js
to get the data two possibilities either with a class using connect and mapStateToProps , or using the useSelector hook with a functional component.
with a Class
import React, { Component } from "react";
import { connect } from "react-redux";
class OutputComponent extends Component {
render = () => {
return (
<div>
<div>{this.props.nameState}</div>
</div>
);
};
}
const mapStateToProps = state => state;
export default connect(mapStateToProps)(OutputComponent);
with a functional component
import React from "react";
import { useSelector } from "react-redux";
const OutputComponent = () => {
const nameState = useSelector((state) => state.nameState);
return (
<div>
<div>{nameState}</div>
</div>
);
};
export default OutputComponent;
Of course you must not forget to create a strore and to provide it to the highest component
store.js
import { createStore } from "redux";
import nameReducer from "./nameReducer";
const store = createStore(nameReducer);
export default store;
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import store from "./store";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Component
const AddTodo = () => {
const [todo, setTodo] = useState("");
const dispatch = useDispatch();
const handleChange = (e) => setTodo(e.target.value);
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addTodoAction(todo));
}
return {
<form onSubmit={handleSubmit}>
<input type="text" onChange={handleChange} />
</form>
}
)
Actions
const addTodoAction = (text) => {
dispatch({
type: "ADD_TODO",
payload: text
})
}
Reducers
const addTodoReducer = (state, action) => {
switch(action.type) {
case "ADD_TODO":
return {
todo: action.payload,
}
default:
return state;
}
}
store
// some code for store.js
Accessing this todo from another component
const ComponentA = () => {
const {todo} = useSelector(state => state.todo);
return (
<p> {todo} </p>
)
}
Side Note:
Redux comes with too much boilerplate if you want to pass text from one component to another, just use props

redux hook fetch data from API with action in separate file

I'm learning redux hooks from library "react-redux" because I need to apply Redux also in the functional components of my project.
So far I don't understand how can be used the same project structure of the redux HOC with connect that I use for the class components.
Specifically I have a separate action file which invoke my API with axios:
FoosActions.js
import axios from "axios";
import {
GET_FOO,
} from "./Types";
};
export const getFoo = () => async (dispatch) => {
const res = await axios.get("/api/v1/foos");
dispatch({
type: GET_FOO,
payload: res.data,
});
};
FooList.js:
import { connect } from "react-redux";
import { getFoos } from "../../actions/FoosActions";
class FoosList extends Component {
constructor() {
super();
this.state = {
errors: {},
};
}
componentDidMount() {
this.props.getFoos();
}
render() {
const { data } = this.props.foo;
return (
<div className="container">
<h2>foo data fetched from API endpoint : </h2>
<ul>
{data.map((foo) => {
return (
<li>
{foo.id} - {foo.name}
</li>
);
})}
<ul>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({
foo: state.foo,
errors: state.errors,
});
export default connect(mapStateToProps, { getFoos })(FooList);
FooReducer,js
import { GET_FOO} from "../actions/Types";
const initialState = {
foos: [],
};
export default function (state = initialState, action) {
switch (action.type) {
case GET_FOO:
return {
...state,
foos: action.payload,
};
Now instead in my Functional Component:
FooListFC.js
import { useDispatch, useSelector } from "react-redux";
import { getFoo } from "../../actions/FoosActions";
const Mapping = (props) => {
const [foo, setFoo] = useState([]);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getFoo());
const fooRetrieved = useSelector((state) => state.foo);
setFoo(fooRetrieved);
}, []);
return (
<div className="container">
<h2>foo data fetched from API endpoint : </h2>
<ul>
{foo.map((foo) => {
return (
<li>
{foo.id} - {foo.name}
</li>
);
})}
</ul>
</div>
)
}
How can I reproduce the same behavior of fetching data from API in class component with actions in a different file and using redux hooks (my code in the functional component is not working) ?
Is it a bad practice having both approaches in the same project?
you are able to reproduce the same behaviour, in the function component you can use the selector only instead of both useSelector and useState:
const Mapping = (props) => {
const foo = useSelector((state) => state.foo);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getFoo());
}, []);
...

Redux store Array to Div list of elements

I have a list of object stored as a array in my redux store which loads on component mount. I want to List them in a div, also to do the crud Operation. This is my implementation. Whenever I use useSelector to save the list for a constants it fectching infinite number of logs.
BranchAction.js
import axios from 'axios';
export const fetchAllBranchListOk = (branchList) => {
return {
type : 'FETCH_ALL_BRANCH_LIST_OK',
branchList
}
};
export const fetchAllBranchList = () =>{
return (dispatch) => {
return axios.get(`https://jsonplaceholder.typicode.com/posts`)
.then(response => {
dispatch(fetchAllBranchListOk(response.data));
})
.catch(error => {
throw(error);
});
}
};
BranchReducer
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_ALL_BRANCH_LIST_OK' :
return action.branchList;
default:
return state;
}
};
BranchManagement.js
function BranchManagement() {
store.dispatch(BranchAction.fetchAllBranchList());
const AllBranch = useSelector(state => state.BranchReducer)
return(
<div>
</div>
)
}
export default BranchManagement;
CombinedReducer -> index.js
import {combineReducers} from 'redux'
import BranchReducer from "./Admin/BranchReducer";
const Reducers = combineReducers({
BranchReducer
});
export default Reducers;
If you want to dispatch the action to fetch the data from the backed, you should be keeping those calls in useEffect hook. The purpose of useEffect is similar to the purpose of Lifecycle methods in the class component like componentDidMount, componentDidUpdate and componentWillUnMount. To understand more about useEffect please refer this.
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import BranchAction from "/path/to/BranchAction";
function BranchManagement() {
const dispatch = useDispatch();
//Since the data in the state is on `branchList`. You can directly return
//`state.branchList` which will you provide you the data you are looking for.
const branchList = useSelector(state => state.branchList)
//It'll act similar to `componentDidMount`. Since we are passing `[]`
//to `useEffect` dependencies array
useEffect(() => {
dispatch(BranchAction.fetchAllBranchList());
}, [])
//Here I'm assuming `branchList` is array of objects with `name` and `id`.
//Updated answer with branchList as[{"branchID":1,"createdBy":1,"isActive":true,"branchDetails":{"branchDetailsID":1}},{"branchID":2,"createdBy":1,"isActive":true,"branchDetails":{"branchDetailsID":1}}]
return(
<div>
{
(branchList || []).map((branch, index) => {
<div key={branch.branchID || index}>
<span>{branch.branchID}</span>
<span>{branch.createdBy}</span>
<span>{branch.isActive}</span>
<span>{branch.branchDetails.branchDetailsID}</span>
</div>
}
}
</div>
)
}
export default BranchManagement;
Hope this helps in order to resolve the issue.

Redux api calling

I'm wanting to update my trending array with the results calling the tmdb api. I'm not sure if im going about this the wrong way with calling the api or if im messing up somewhere else along the way. So far I've really been going in circles with what ive tried. Repeating the same things and not coming to a real solution. Havent been able to find another question similar to mine.
my actions
export const getTrending = url => dispatch => {
console.log("trending action");
axios.get(url).then(res =>
dispatch({
type: "TRENDING",
payload: res.data
})
);
};
my reducer
const INITIAL_STATE = {
results: [],
trending: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case "SEARCH_INFO":
return {
results: [action.payload]
};
case "TRENDING":
return { trending: action.payload };
default:
return state;
}
};
and my component im trying to get the results from
import React, { Component } from "react";
import Trending from "./Treding";
import "../App.css";
import { getTrending } from "../actions/index";
import { connect } from "react-redux";
export class Sidebar extends Component {
componentDidMount = () => {
const proxy = `https://cors-anywhere.herokuapp.com/`;
getTrending(`${proxy}https://api.themoviedb.org/3/trending/all/day?api_key=53fbbb11b66907711709a6f1e90fc884
`);
};
render() {
return (
<div>
<h3 className="trending">Trending</h3>
{
this.props.trending ? (
<Trending movies={this.props.trending} />
) : (
<div>Loading</div>
)}
</div>
);
}
}
const mapStateToProps = state => {
return {
trending: state.trending
};
};
export default connect(mapStateToProps)(Sidebar);
Since you are directly calling the getTrending without passing it to connect method, it might be the issue.
Instead that you can pass getTrending to connect method so it can be available as props in the component. After that it can be dispatched and it will be handled by redux/ redux-thunk.
export default connect(mapStateToProps, { getTrending })(Sidebar);
And access it as props in the component.
componentDidMount = () => {
// const proxy = `https://cors-anywhere.herokuapp.com/`;
this.props.getTrending(`https://api.themoviedb.org/3/trending/all/day?api_key=53fbbb11b66907711709a6f1e90fc884
`);
};

Resources