I'm trying to add Redux to my test app but I'm having trouble fetching data from my API. It feels like I've gone through all of the steps but I can't access the props from the fetch in my component, so I'm messing up somewhere along the way. My code:
actions/index.js:
import 'whatwg-fetch';
import ReduxThunk from 'redux-thunk';
export const FETCH_RECIPES = 'FETCH_RECIPES';
const ROOT_URL = 'http://myapi/recipe/';
export function fetchRecipes(id) {
const url = ROOT_URL + "0";
// const request = fetch(url);
return (dispatch) => {
fetch(url)
.then((response) => response.json())
.then((request) => dispatch(fetchRecipesSuccess(request)))
};
}
export function fetchRecipesSuccess(request) {
return {
type: FETCH_RECIPES,
request
};
}
reducer_recipe.js:
import { FETCH_RECIPES } from "../actions/index";
export default function(state = [], action) {
switch(action.type) {
case FETCH_RECIPES:
return [action.payload.data, ...state];
}
return state;
}
reducers/index.js:
import { combineReducers } from 'redux';
import RecipesReducer from './reducer_recipes';
const rootReducer = combineReducers({
recipes: RecipesReducer
})
export default rootReducer;
aaaaand in my component I'm using this code:
function mapStateToProps({ recipes }) {
return { recipes };
}
connect(mapStateToProps, {fetchRecipes})(Recipe);
And in my index.js I'm creating my store with const createStoreWithMiddleware = createStore(reducers, applyMiddleware(ReduxPromise));
With my thinking I should be good to use this.props to access the data I've fetched from my API but I guess I'm dropping the data somewhere along the way. What am I missing?
Check your reducer well. You seem to be returning action.payload.data whereas in your fetchRecipesSuccess, it's named request. And you can console.log the action object to see what you've got
import { FETCH_RECIPES } from "../actions/index";
export default function(state = [], action) {
switch(action.type) {
case FETCH_RECIPES:
// Verify here that your request object has data
return [...state, action.request.data];
// Default state
default:
return state;
}
}
Hope this helps!
Related
I am new to Next.js, So I follow some tutorials for Redux integration in Next.js. All is working fine but whenever I switch between pages, each time API make a call, and Redux lost its stored value.
The basic function is like this. Whenever a user loads a website an API call will fetch category data from the server and save that data in reducer[categoryReducer], then the user can navigate to any page and category data will fetched from the reducer. But in my case, it hits again and again
Full Code:
// Action Call
import * as Constants from '../../constant/constant';
import * as t from '../types';
import axios from 'axios';
export const loadCategoryApi = (type) => dispatch => {
axios.post(Constants.getCategories,type)
.then(function (response) {
console.log(response);
if(response && response.data && response.data.status==="200"){
dispatch({
type: t.LOAD_CATEGORY,
value: type
});
}
else if(response && response.data && response.data.status==="404"){
alert('Someting went wrong');
}
})
}
// Reducer File
import * as t from '../types';
const initialState = {
doc:null
}
const CategoryReducer = (state = initialState, action) =>{
console.log('reducer action', action.type);
switch (action.type){
case t.LOAD_CATEGORY:
console.log('slots actions',action);
return({...state, doc:action.value})
default:
return state;
}
}
export default CategoryReducer;
// Store file
import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import { createWrapper } from "next-redux-wrapper"
import rootReducer from "./reducers/rootReducer"
const middleware = [thunk]
const makeStore = () => createStore(rootReducer, compose(applyMiddleware(...middleware)))
export const wrapper = createWrapper(makeStore);
// rootReducer
import { combineReducers } from "redux"
import CategoryReducer from "./CategoryReducer";
const rootReducer = combineReducers({
CategoryReducer: CategoryReducer
})
export default rootReducer;
// _app.js
import React from "react"
import { wrapper } from "../redux/store"
import Layout from '../components/Layout';
import '../styles/globals.css'
const MyApp = ({ Component, pageProps }) =>(
<Layout>
<Component {...pageProps} />
</Layout>
);
export default wrapper.withRedux(MyApp);
// Uses
import React, { useState, useEffect } from 'react';
import {connect} from "react-redux";
import {loadCategoryApi} from "../redux/actions/CategoryAction";
function navbar(props){
const { loadCategory, loadCategoryApi } = props;
useEffect(() => {
if(loadCategory===null){
console.log('navbar loading funciton');
loadCategoryFunation();
}
}, []);
const loadCategoryFunation = () =>{
var json = {
type : 'main'
};
loadCategoryApi(json);
}
}
const mapStateToProps = state => {
return { loadCategory: state.CategoryReducer.doc }
}
const mapDispatchToProps = {
loadCategoryApi
}
export default connect(mapStateToProps, mapDispatchToProps)(Navbar)
What I am doing wrong?
You have to create main reducer to handle the hydration. I explained this hydration process here.
In the file that you created the store, write main reducer
import reducers from "./reducers/reducers";
const reducer = (state, action) => {
// hydration is a process of filling an object with some data
// this is called when server side request happens
if (action.type === HYDRATE) {
const nextState = {
...state,
...action.payload,
};
return nextState;
} else {
// whenever we deal with static rendering or client side rendering, this will be the case
// reducers is the combinedReducers
return reducers(state, action);
}
};
then pass this reducer to the store
I'm creating a simple blog using React and Redux, most for learning these two libraries. Everything is working fine, my only question is about the way the state object is structured in the application. When I go to the Redux Toolkit, I see this:
Redux Toolkit screenshot
In the state I have a post object with another post object inside it. My question is: where did I defined the state in that way (with a post object inside a post object)? Below are the content of this application:
MainPost.js
import React, { useEffect } from 'react'
import { connect, useDispatch } from 'react-redux'
import { getPostListAction } from '../store/actions/getPostListAction'
export const MainPost = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(getPostListAction())
})
return (
<div>App</div>
)
}
const mapStateToProps = state => {
return {
post: state.post
}
}
export default connect(mapStateToProps)(MainPost)
store.js
import { createStore, applyMiddleware, compose } from 'redux'
import rootReducer from './reducers/rootReducer'
import { devToolsEnhancer } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import { api } from './middleware/api'
const store = createStore(
rootReducer,
compose(
devToolsEnhancer(),
applyMiddleware(thunk),
)
)
export default store
postListReducer.js
const initialState = {
post: ''
}
export default function getPostListReducer(state = initialState, action) {
if(action.type === 'getPostList') {
return {
...state,
post: action.payload
}
} else {
return state
}
}
The first post (after state) is namespace of postListReducer
Here is how you use combineReducer to create a rootReducer:
const rootReducer = combineReducers({
post: postListReducer,
other: otherReducer
})
And to select data from the store, you do:
const mapStateToProps = state => {
return {
post: state.post.post // the first "post" (after "state") is namespace of postListReducer
}
}
Or if you don't want to write state.post.post, you can change your postListReducer to directly hold the "post" data:
const initialPost = ''
export default function getPostListReducer(state = initialPost, action) {
if(action.type === 'getPostList') {
return action.payload
} else {
return state
}
}
I'm having a really strange issue in react in which my reducers seem to be modifying parts of the state that they shouldn't, I'm sure this is due to some oversight in my implementation, but I can't track it down.
I currently have one store, two actions/action creators, two reducers, and one root reducer. Here is my code, (left out some unimportant details):
//actionTypes.ts
export const SUBMIT_SEARCH = "SUBMIT_SEARCH";
export const CHANGE_SEARCH_PAGE = "CHANGE_SEARCH_PAGE";
-
//actionCreators.ts
import { SUBMIT_SEARCH, CHANGE_SEARCH_PAGE } from "./actionTypes"
export const submitSearch = (submitSearch) => ({
type: SUBMIT_SEARCH,
submitSearch
});
export const changeSearchPage = (changeSearchPage) => ({
type: CHANGE_SEARCH_PAGE,
changeSearchPage
});
-
//submitSearchReducer.ts
import { SUBMIT_SEARCH } from "../actions/actionTypes";
const submitSearch = (state = {}, action) => {
switch (action.type) {
case SUBMIT_SEARCH:
return {
...state,
submitSearch: action.submitSearch
}
default:
return state;
};
};
export default submitSearch;
-
//changeSearchPageReducer.ts
import { CHANGE_SEARCH_PAGE } from "../actions/actionTypes";
const changeSearchPage = (state = {}, action) => {
switch (action.type) {
case CHANGE_SEARCH_PAGE:
return {
...state,
changeSearchPage: action.changeSearchPage
}
default:
return state;
};
};
export default changeSearchPage;
-
//rootReducer.ts
import { combineReducers } from 'redux';
import submitSearch from "../reducers/submitSearchReducer"
import changeSearchPage from "../reducers/submitSearchReducer";
const rootReducer = combineReducers({
submitSearch: submitSearch,
changeSearchPage: changeSearchPage
});
export default rootReducer;
-
I create the store like this:
const logger = createLogger();
const store: Store<any> = createStore(rootReducer, {/*Initial state empty*/}, applyMiddleware(logger));
-
The general flow is like this:
User enters a search string on the page which triggers a store.dispatch(submitSearch(string)) call
User enters a number on the page which triggers a store.dispatch(changeSearchPage(newPage))
Here's the output from my logger:
[![Search call][1]][1]
[![change page call][2]][2]
You can see here that the state is clearly getting mixed up, and the wrong data is going to the changeSearchPage key.
What is causing this mix up?
In the rootReducer you're importing the same reducer twice
I've just started implementing Redux in a React application, and it's the first time i try to, so please bear with me.
My problem is that i can't access the data in my component this this.props.questions
I have a simple action which is supposed to async fetch some data
export function fetchQuestions(url) {
const request = axios.get('url');
return (dispatch) => {
request.then(({data}) => {
dispatch({ type: 'FETCH_QUESTIONS', payload: data });
console.log(data);
});
};
}
Which is picked up my reducer questions_reducer
export default function(state = [], action) {
switch(action.type) {
case 'FETCH_QUESTIONS':
console.log('Getting here');
return state.concat([action.payload.data]);
console.log('But not here');
}
return state;
}
My index reducer looks like this:
import { combineReducers } from 'redux';
import fetchQuestions from './question_reducer';
const rootReducer = combineReducers({
questions: fetchQuestions
});
export default rootReducer;
I pass it to my store where i apply the thunk middleware and finally into <Provider store={store}> which wraps my app, but the prop just returns undefined in my React component
configureStore:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
I don't know if the console.log is to be trusted but it logs from my questions_reducer before the data is returned from the dispatch in my action
EDIT (Component)
class QuestionsRoute extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
this.props.fetch('someUrl);
setTimeout(function(){ console.log(this.props.questions) },
1500);
}
render() {
{console.log(this.props.questions)}
return (
<div>
<1>Hello</1>
{this.props.questions !== undefined ?
<p>We like props</p>: <p>or not</p>
}
</div>
);
}
};
const mapStateToProps = (state) => {
return {
questions: state.questions,
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetch: () => dispatch(fetchQuestions())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(QuestionsRoute);
In your reducer
export default function(state = [], action) {
switch(action.type) {
case 'FETCH_QUESTIONS':
return state.concat([action.payload.data]);
}
return state;
}
You should probably instead have return state.concat([action.payload]);
Since from dispatch({ type: 'FETCH_QUESTIONS', payload: data }); we see that payload is data, it doesn't contain it.
Update: I'd recommend setting up redux-devtools / redux-devtools-extension / react-native-debugger so you can visually see your actions and store state live - makes things like this a lot easier to debug!
Fairly new to redux, and have gone through the official guides. Now I'm trying to do something solo. I have two reducers and am using react-thunk. When I dispatch an action after the first one it clears my collection of my other reducer. To illustrate what I mean is I have:
Actions.js
import axios from 'axios';
function fetchAtms() {
return axios.get('http://localhost:4567');
}
export const recievedAtms = (atms) => {
return {
type: 'RECIEVED_ATMS',
atms
}
}
export const completed = () => {
return {
type: 'COMPLETED',
}
}
export const loadMore = () => {
return {
type: 'LOAD_MORE',
}
}
export const loadAtms = (forPerson) => {
return function (dispatch) {
return fetchAtms().then((response) => {
let atms = response.data.map((item) => {return item['location']})
dispatch(recievedAtms(atms));
// When dispatch(completed()); is called
// it is clears my app collection.
dispatch(completed());
// $r.store.getState() => Object {app: {atms: []}, isLoading: false, router: Object}
}, (error) => {
console.log('implement me');
})
}
}
Reducers
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
const app = (state = {}, action) => {
switch (action.type) {
case 'RECIEVED_ATMS':
return {
atms: action.atms
}
default:
return {};
}
}
const isLoading = (state = true, action) => {
switch (action.type) {
case 'COMPLETED':
return !state;
default:
return state;
}
}
const appReducer = combineReducers({
app,
isLoading,
router: routerReducer
});
export default appReducer;
Store.js
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware} from 'react-router-redux';
import createHistory from 'history/createBrowserHistory';
import thunk from 'redux-thunk';
import appReducer from './reducers/app';
export const history = createHistory()
const middleware = routerMiddleware(history);
const store = createStore(appReducer, applyMiddleware(middleware, thunk));
export default store;
If you hone in on Actions.js where in the loadAtms function I:
Fetch my atms
Dispatch receivedAtms
Dispatch Completed
When I dispatch completed() it clear my atms collection. I'm not entirely sure. I would not expect that since the states between the two reducers are separate. My expectation is:
After I've fired completed() I do not expect it to clear my collection of atms. The resulting state after calling completed() should look like this:
{
isLoading: false,
app: {atms: [{id: 1}, {id: 2}, {id: 3}]}
}
currently what is happening is this:
{isLoading: false, app: {}}
Any thoughts on what I may have done wrong here.
Your atms reducer is returning {} if the action isn't one it is looking for. Instead, you should be returning state I believe. So:
const app = (state = {}, action) => {
switch (action.type) {
case 'RECIEVED_ATMS':
return {
atms: action.atms
}
default:
return state;
}
}