I am new to redux and reactjs. I am trying to use a state data called type in the link I am fetching using axios in line no 17.
I am setting the value of type from another .jsx file using dispatch().
Here in Home.jsx file I am calling dispatch in line no 24 for updating the state value type with onClick event.
Home.jsx
import React from 'react';
import '../styles/home.scss';
import { Link } from 'react-router-dom';
import { setType } from '../redux/imageSlice';
import { useDispatch } from 'react-redux';
const Home = () => {
const dispatch = useDispatch();
return (
<div className="container">
<div className="row">
<div className="col-sm-6 col-md-4 d-flex justify-content-center my-3">
<div className="ci">
<img
className="img-fluid"
src="https://res.cloudinary.com/djz3p8sux/image/upload/v1662295247/web-projects-images/natures_hodrkk.jpg"
alt="Nature"
/>
<Link to="/nature">
<div className="middle">
<div
className="text"
onClick={() => dispatch(setType('nature'))}>
Nature
</div>
</div>
</Link>
</div>
</div>
</div>
</div>
);
};
export default Home;
imageSlice.js
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import axios from 'axios';
const initialState = {
type: '',
images: [],
error: null,
isLoading: false,
};
const config = {
Authorization: '563492ad6f91700001000001350d302e175b4c208aac413953d6edcc',
};
export const fetchImages = createAsyncThunk('images/fetchImages', async () => {
const res = await axios.get(
`https://api.pexels.com/v1/search?query=${initialState.type}&per_page=15`,
{
headers: config,
}
);
return res.data;
});
export const imageSlice = createSlice({
name: 'images',
initialState,
reducers: {
setType: (state, action) => {
state.type = action.payload;
},
},
extraReducers: builder => {
builder.addCase(fetchImages.pending, state => {
state.isLoading = true;
});
builder.addCase(fetchImages.fulfilled, (state, action) => {
state.isLoading = false;
state.images = action.payload;
state.error = null;
});
builder.addCase(fetchImages.rejected, (state, action) => {
state.isLoading = false;
state.images = [];
state.error = action.error.message;
});
},
});
export const { setType } = imageSlice.actions;
export default imageSlice.reducer;
store.js
import { configureStore } from '#reduxjs/toolkit';
import imageReducer from './imageSlice';
export const store = configureStore({
reducer: {
images: imageReducer,
},
});
How to do that? I am trying to update the fetching link using state type value on line 17 in imageSlice.js file.
One option is to provide the type to fetchImages by parameters (see payload):
export const fetchImages = createAsyncThunk('images/fetchImages', async (payload) => {
const res = await axios.get(
`https://api.pexels.com/v1/search?query=${payload.type}&per_page=15`,
{
headers: config,
}
);
return res.data;
});
Then you need to provide the type to the function, when calling fetchImages. Here in an example component:
import { fetchImages } from '../redux/imageSlice';
import { useDispatch, useSelector } from 'react-redux';
function YourCallingComponent(props) {
const dispatch = useDispatch();
const currentType = useSelector((state) => state.type);
function fetchImagesOfType() {
dispatch(fetchImages({
type: currentType
}));
}
return (
...
)
}
Related
I am trying to store users fetched by random users api and trying to dispatch it to my store i have store users with an empty array, when i am trying to get users using useSelector i am getting an undefined object.
Here is my store.js:
import { configureStore } from "#reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
import userReducer from "../features/users/userSlice";
export const store = configureStore({
reducer: {
counter: counterReducer,
user: userReducer,
},
});
Here is userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name: "users",
initialState: {
userArray: [],
},
reducers: {
getUsers: (state, action) => {
state.userArray = action.payload;
}
}
})
export const { getUsers } = userSlice.actions;
export const selectUsers = (state) => state.users.userArray;
export default userSlice.reducer;
Here is App.js
import logo from './logo.svg';
import './App.css';
import {useSelector, useDispatch } from 'react-redux';
import { getUsers, selectUsers } from './features/users/userSlice';
function App() {
const dispatch = useDispatch();
const users = useSelector(selectUsers);
const fetchUsers = async () => {
fetch("https://gorest.co.in/public/v2/users")
.then((response) => response.json())
.then((data) => {
console.log("data=====", data);
dispatch(getUsers(data));
});
};
return (
<div className="App">
<header className="App-header">
<button onClick={fetchUsers}>Get Users</button>
{users.length > 0 &&
users.map((user) => {
<li>user.name</li>;
})}
</header>
</div>
);
}
export default App;
you are mixing up state.user and state.users
either rename user in configureStore to users or use state.user.userArray
am in a fixed currently, I have my store setup, actions, and reducers, also my form component where I send the dispatch action to the reducer. I get a TypeError: Object(...) is not a function. I have tried everything I can but it's probably I've not fully understood the technology. The following are my files
Form component I connected with Redux with connect
import React, { useState } from 'react';
import { connect } from 'react-redux';
import addBrandCommission from '../redux/actions/brandCommissionActions';
const AddBrandCommission = () => {
const [formData, setFormData] = useState({
name: '',
commission: '',
});
const { name, commission } = formData;
const handleChange = (e) => setFormData({ ...formData, [e.target.name]: e.target.value });
const handleSubmit = (e) => {
console.log('hi me');
e.preventDefault();
if (formData) {
console.log(formData);
addBrandCommission({
name,
commission,
});
console.log('action is called, addBrandCommission');
setFormData('');
}
};
return (
<>
<h4>here, we will add commision here</h4>
<form method="POST" onSubmit={handleSubmit}>
<div className="form-group">
<input type="text" name="name" value={name} onChange={handleChange} placeholder="Enter brand here" required />
</div>
<div className="form-group">
<input type="decimal" name="commission" value={commission} onChange={handleChange} placeholder="Enter commission here" required />
</div>
<div className="form-group">
<button type="submit" name="addBrandComission">Add Brand and Commission</button>
</div>
</form>
</>
);
};
export default connect(null, { addBrandCommission })(AddBrandCommission);
This is my 'addForm' reducer function
import { ADD_BRANDCOMMISSION, BRANDCOMMISSION_ERRORS } from '../actions/actionTypes';
const initialState = [{
name: 'Universal',
commission: '12.8',
}];
const addBrandCommissionReducer = (state = initialState, action) => {
const { type, payload } = action;
console.log(action);
switch (type) {
case ADD_BRANDCOMMISSION:
return {
...state,
brandCommission: [
{
name: payload.name,
commission: payload.commission,
},
],
};
case BRANDCOMMISSION_ERRORS:
return {
...state,
error: payload,
loading: false,
};
default:
return state;
}
};
export default addBrandCommissionReducer;
Here is my actions
import { ADD_BRANDCOMMISSION, BRANDCOMMISSION_ERRORS } from './actionTypes';
const addBrandCommission = (brandCommission) => ({
type: ADD_BRANDCOMMISSION,
payload: { brandCommission },
});
const brandCommissionErrors = () => ({
type: BRANDCOMMISSION_ERRORS,
});
export default { addBrandCommission, brandCommissionErrors };
Here is my store
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = { };
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware), // store enhancer func
// eslint-disable-next-line no-underscore-dangle
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()),
);
export default store;
Here is my actionTypes
export const ADD_BRANDCOMMISSION = 'ADD_BRANDCOMMISSION';
export const BRANDCOMMISSION_ERRORS = 'BRANDCOMMISSION_ERRORS';
export const LOGGEDIN = 'LOGGEDIN';
export const LOGIN_ERRORS = 'LOGIN_ERRORS';
export const SET_USER = 'SET_USER';
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
Thank you to anyone who will one way or the other share their knowledge about how to fix this code :man_technologist: :muscle:
I console.log(action.payload)in the reducer and it returned undefined
You import action wrong way. Update like this to fix:
import { addBrandCommission } from '../redux/actions/brandCommissionActions';
Getting a weird error where 'map' is undefined. I'm not sure if my functions are firing at the wrong time and that's resulting in no data being received.
I'm adding Redux into my simple little application that just pulls data from an API and displays it. It's a list of a bunch of Heroes. Like I said before, I think that the error is coming from different times in the ansyc API call and when Redux is firing. But then again I'm a novice so any help is much appreciated.
import React, {useEffect} from 'react'
import { connect } from 'react-redux'
import { fetchHeroes } from '../actions/heroesActions'
import { Hero } from '../components/Hero'
const HeroesPage = ({ dispatch, loading, heroes, hasErrors }) => {
useEffect(() => {
dispatch(fetchHeroes())
}, [dispatch])
const renderHeroes = () => {
if (loading) return <p>Loading posts...</p>
if (hasErrors) return <p>Unable to display posts.</p>
return heroes.map(hero => <Hero key={hero.id} hero={hero} />)
}
return (
<section>
<h1>Heroes</h1>
{renderHeroes()}
</section>
)
}
// Map Redux state to React component props
const mapStateToProps = state => ({
loading: state.heroes.loading,
heroes: state.heroes.heroes,
hasErrors: state.heroes.hasErrors,
})
export default connect(mapStateToProps)(HeroesPage)
export const GET_HEROES = 'GET HEROES'
export const GET_HEROES_SUCCESS = 'GET_HEROES_SUCCESS'
export const GET_HEROES_FAILURE = 'GET_HEROES_FAILURE'
export const getHeroes = () => ({
type: GET_HEROES,
})
export const getHeroesSuccess = heroes => ({
type: GET_HEROES_SUCCESS,
payload: heroes,
})
export const getHeroesFailure = () => ({
type: GET_HEROES_FAILURE,
})
export function fetchHeroes() {
return async dispatch => {
dispatch(getHeroes())
try {
const response = await fetch('https://api.opendota.com/api/heroStats')
console.log(response)
const data = await response.json()
dispatch(getHeroesSuccess(data))
} catch (error) {
dispatch(getHeroesFailure())
}
}
}
index.js where I created the store
// External imports
import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
// Local imports
import App from './App'
import rootReducer from './reducers'
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)))
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
import React, {useEffect} from 'react'
import { useSelector } from 'react-redux'
import { fetchHeroes } from '../actions/heroesActions'
import { Hero } from '../components/Hero'
const HeroesPage = () => {
const data = useSelector(state => state.heroes);
useEffect(() => {
fetchHeroes();
}, [])
const renderHeroes = () => {
if (data.loading) return <p>Loading posts...</p>
if (data.hasErrors) return <p>Unable to display posts.</p>
return data.heroes.map(hero => <Hero key={hero.id} hero={hero} />)
}
return (
<section>
<h1>Heroes</h1>
{renderHeroes()}
</section>
)
}
export default HeroesPage
action file
// import store from your createStore file and access dispatch from it
dispatch = store.dispatch
export const GET_HEROES = 'GET HEROES'
export const GET_HEROES_SUCCESS = 'GET_HEROES_SUCCESS'
export const GET_HEROES_FAILURE = 'GET_HEROES_FAILURE'
export const getHeroes = () => ({
type: GET_HEROES,
})
export const getHeroesSuccess = heroes => ({
type: GET_HEROES_SUCCESS,
payload: heroes,
})
export const getHeroesFailure = () => ({
type: GET_HEROES_FAILURE,
})
export const fetchHeroes = () => {
dispatch(getHeroes())
try {
const response = await fetch('https://api.opendota.com/api/heroStats')
console.log(response)
const data = await response.json()
dispatch(getHeroesSuccess(data))
} catch (error) {
dispatch(getHeroesFailure())
}
}
reducer file
import * as actions from '../actions/heroesActions'
export const initialState = {
heroes: [],
loading: false,
hasErrors: false,
}
export default function heroesReducer(state = initialState, action) {
switch (action.type) {
case actions.GET_HEROES:
return { ...state, loading: true }
case actions.GET_HEROES_SUCCESS:
return { heroes: action.payload, loading: false, hasErrors: false }
case actions.GET_HEROES_FAILURE:
return { ...state, loading: false, hasErrors: true }
default:
return state
}
}
I am trying to fetch data from an api, but I am getting an error: TypeError: this.props.getPeople is not a function, while everything looks good through the code, as below:
people-component.js
import React, { Component } from 'react';
import './people-component-styles.css';
import { Container, Row, Col, Card } from 'react-bootstrap';
import { connect } from 'react-redux';
import { getPeople } from '../../actions/index'
import 'react-lazy-load-image-component/src/effects/blur.css';
import 'animate.css/animate.min.css';
export class People extends Component {
componentDidMount() {
// console.log(this.props);
this.props.getPeople();
}
render() {
// console.log(this.props);
return (
<Row className='main'>
hello!
</Row>
);
}
}
const mapStateToProps = state => ({
people: state.people
})
const mapDispatchToProps = dispatch => ({
getPeople: () => dispatch(getPeople())
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(People);
actions/index.js
export const getPeople = () => {
console.log("yes");
return async dispatch => {
const response = await fetch('https://dma.com.eg/api.php?action=getPeople', {
method: 'GET'
})
const json = await response.json();
dispatch({ type: "GET_PEOPLE", payload: json });
}
}
reducers/index.js
const INITIAL_STATE = {
people: []
}
const rootReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case "GET_PEOPLE":
return ({
...state,
people: state.people.concat(action.payload)
})
default:
return state;
}
};
export default rootReducer
I was importing People component as name while it's exported as default, thanks to #Brian Thompson .. it's fixed.
i used react 16+ and redux get jsonplaceholder fake data to assign posts state but not working. can't assign the state. how can i assign json values into state using concat method. i check lifecycle methods also but can't get the answer.
Reducer
import * as actiontypes from './actions';
import axios from 'axios';
const initalstate = {
counter: 0,
posts: []
};
const reducer = (state = initalstate, action ) => {
switch (action.type) {
case actiontypes.actionFetchPost:
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
return {
...state,
posts: state.posts.concat(res.data)
}
});
break;
default :
return state;
}
};
export default reducer;
Redux reducers must be pure functions, it means they should not contain any side effects like calling api.
You need to call api in action creators using redux-thunk package.
Codesandbox
An example action creator:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_FAILURE,
FETCH_POSTS_SUCCESS
} from "./actionTypes";
import axios from "axios";
export const fetchPosts = () => {
return dispatch => {
dispatch(fetchPostsStarted());
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then(res => {
dispatch(fetchPostsSuccess(res.data));
})
.catch(err => {
dispatch(fetchPostsFailed(err.message));
});
};
};
const fetchPostsStarted = () => {
return {
type: FETCH_POSTS_STARTED,
payload: {
isLoading: true
}
};
};
const fetchPostsSuccess = posts => {
return {
type: FETCH_POSTS_SUCCESS,
payload: {
posts
}
};
};
const fetchPostsFailed = error => {
return {
type: FETCH_POSTS_FAILURE,
payload: {
error
}
};
};
And reducer file:
import {
FETCH_POSTS_STARTED,
FETCH_POSTS_SUCCESS,
FETCH_POSTS_FAILURE
} from "../actions/actionTypes";
const initialState = {
posts: [],
loading: false,
error: null
};
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_POSTS_STARTED:
return {
...state,
loading: true
};
case FETCH_POSTS_SUCCESS:
return {
...state,
loading: false,
error: null,
posts: action.payload.posts
};
case FETCH_POSTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error
};
default:
return state;
}
}
In store we use redux-thunk like this:
import { createStore, compose, applyMiddleware, combineReducers } from "redux";
import reduxThunk from "redux-thunk";
import postsReducers from "./reducers/postsReducers";
const rootReducer = combineReducers({
posts: postsReducers
});
const store = createStore(rootReducer, compose(applyMiddleware(reduxThunk)));
export default store;
Posts component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPosts } from "./store/actions/postsActions";
class Posts extends Component {
componentDidMount() {
this.props.fetchPosts();
}
render() {
const { posts, loading, error } = this.props;
return (
<div>
{loading && <div>LOADING...</div>}
{error && <div>{error}</div>}
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
}
const mapStateToProps = state => {
const { posts, loading, error } = state.posts;
return {
posts,
loading,
error
};
};
export default connect(
mapStateToProps,
{
fetchPosts
}
)(Posts);
Index.js
import ReactDOM from "react-dom";
import store from "./store/store";
import { Provider } from "react-redux";
import Posts from "./Posts";
function App() {
return (
<div className="App">
<Posts />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
Do api call in action and return promise, use redux-thunk and redux-promise-middleware:
export const myApiCall = (args1, arg2) => async (dispatch, getState) => {
const payload = fetch({ ...config });
return dispatch({ type: 'MY_API_CALL', payload });
}
Then in reducer will have to handle two results:
MY_API_CALL_FULFILLED and MY_API_CALL_REJECTED