Redux Fetch JSON Data - javascript

my Redux fetch is returning empty..
It does not break but it just returns me empty object.
Here is the code for my action (newsActions.js):
import axios from 'axios';
import kickstarterData from '../server/kickstarter-october.json';
export const FETCH_KICKSTARTER = 'FETCH_KICKSTARTER';
export function fetchKickstarter() {
return {
type: FETCH_KICKSTARTER,
payload: {
data: kickstarterData
}
};
}
Here is my Reducer:
import { FETCH_KICKSTARTER } from '../actions/kickstarterActions';
export default function(state = [], action) {
switch (action.type) {
case FETCH_KICKSTARTER:
debugger;
return [action.payload.data, ...state];
}
return state;
};
https://stackoverflow.com/questions/ask#
Here is my index.js that combines all the reducers:
import { combineReducers } from 'redux';
import NewsReducer from './reducer_news';
import KickstarterReducer from './reducer_kickstarter';
const rootReducer = combineReducers({
news: NewsReducer,
kickstarters: KickstarterReducer
});
export default rootReducer;
Finally, inside my app.js I have the following code:
const mapStateToProps = (state) => ({
news: state.news,
kickstarters: state.kickstarters
});
export default connect(mapStateToProps, {...newsActions, ...kickstarterActions})(App);
Could anyone tell me why this is breaking?
Also, could anyone suggest me a better/cleaner way of writing these codes?
Thank you

I have a hunch that in your reducer
return [action.payload.data, ...state];
should be
return [...action.payload.data, ...state];
Frankly it should be just
return [...action.payload.data ];
Since i don't have any idea about your biz logic, but later seems more correct to me (why do you need to merge it with old state).
you need to spread the action.payload.data in the state.

Related

Redux does not send to the store 2 properties at a time

I'm new to Redux. I have a reducer with an initial state with 2 properties.
From a component I send data to store, but only one of the properties is delivered - either time or date. But I need to send both.
I tried to do async/await thinking that the interpreter simply runs out of time - even though it is not as much logical, I tried. It did not help me. Also I thought that Redux cannot accept objects but I researched and saw that it can. Then I proved to myself that it really can - I just simply sent only 1 property.
Code snippets are below.
*Reducer: *
const initialState = {
date: {},
time: {}
}
const dateTimeReducer = (state = initialState, action) => {
switch (action.type) {
case 'setDate':
return {
date: action.payload,
}
case 'setPickedCell':
return {
time: action.payload,
}
default:
return state
}
}
export default dateTimeReducer
*index.js*
import React from 'react'
import ReactDOM from 'react-dom'
import './css/style.comp.css'
import App from './App'
import { BrowserRouter } from 'react-router-dom'
import store from './redux/store'
import { Provider } from 'react-redux'
// import rootReducer from './redux/rootReducer'
// const store = createStore(rootReducer)
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root')
)
*store.js*
import { createStore } from 'redux'
import rootReducer from './rootReducer'
const store = createStore(rootReducer)
export default store
*rootReducer.js*
import { combineReducers } from 'redux'
import dateTime from './reducers/dateTime'
export default combineReducers({ dateTime })
*Component - I will simplify, with a condition that I have no syntax mistakes and the code works when I send only 1 of 2 properties to a Redux store*
import Date from ''
import Time from ''
class Comp extends React.Component {
state = {
date: {...},
time: {...} // both date and time already set during rendering cycle - through a function
// acceptStateFromDate and acceptStateFromTime
}
acceptStateFromDate() {
this.setState({ date: stateData })
}
// same about acceptStateFromTime - those functions are used to get state data from a child component and are called while componentDidMount
acceptStateFromTime() {
...
}
onConfirmClick = () => {
this.props.isAllowedHandler()
this.props.onDateSet(this.state.datePicker)
this.props.onCellSet(this.state.timePicker)
}
componentDidMount() {
acceptStateFromDate()
acceptStateFromTime()
}
render() {
return (
<div>
<div onClick={this.onConfirmClick}> Confirm </div>
<Time sendState={acceptStateFromTime} />
<Date sendState={acceptStateFromDate} />
)
}
}
function mapStateToProps(state) {
return {
date: state.dateTime.date,
time: state.dateTime.time,
}
}
function mapDispatchToProps(dispatch) {
return {
onDateSet: (val) => dispatch({ type: 'setDate', payload: val }),
onCellSet: (val) => dispatch({ type: 'setPickedCell', payload: val }),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Appointment))
All exports and imports are present in my code.
So, if I send through onConfirmClick only this.props.onDateSet({someObj})```, I will get in my Redux state date: {someObj}. Same about time - I will get time: {someObj}````.
But if I call those 2 props together, only one will be delivered to the store.
I guess the problem is deeper than the logic, probably I'm not so good enough either in React system or Redux.
Does anyone know where is the problem?
Thanks in advance!
Your reducers are wrong you have to return the state too. At the moment you are completely rewriting it:
return {
date: action.payload,
}
Change your return statement to the following:
return {
...state,
date: action.payload,
}
const dateTimeReducer = (state = initialState, action) => {
switch (action.type) {
case 'setDate':
return {
...state,
date: action.payload,
}
case 'setPickedCell':
return {
...state,
time: action.payload,
}
default:
return state
}
}

Problem with refreshing values in redux reducer. Why can't I create action and assign new values of state?

I can't update my store values using action creator. All needed values I get from component normally, but just can't substitute the initial state values with them. I believe I made a stupid mistake or even misspelled something, but I spent a lot of time already, and the console in the browser still shows me the empty strings in that reducer's state.
let initialstate = {
login: "",
password: ""
}
const formReducer = (state = initialstate ,action) => {
switch (action.type) {
case 'SET-FORM-DATA': {
return {
...state,
login: action.login,
password: action.password
}
}
default: return state;
}
}
export const SetFormData = (login, password) => ({
type: 'SET-FORM-DATA', login, password
})
export default formReducer;
That formReducer is normally assigned in redux-store
import { applyMiddleware, combineReducers, createStore } from 'redux';
import authReducer from './auth-reducer';
import dialogsReducer from './dialogs-reducer';
import profileReducer from './profile-reducer';
import usersReducer from './users-reducer';
import thunkMiddleware from 'redux-thunk';
**import formReducer from './form-reducer';**
let reducers = combineReducers({
profilePage: profileReducer,
dialogPage: dialogsReducer,
usersPage: usersReducer,
auth: authReducer,
form: formReducer
})
let store = createStore(reducers, applyMiddleware(thunkMiddleware));
window.store = store;
export default store;
I call this action creator on submit of form like that:
const onSubmit = formData => {
props.SetFormData(formData.Login, formData.password)
}
And this call seems to be, luckily works alright
I am Brazilian and therefore I speak Portuguese, but I will use the translator to try to help you.
I didn't quite understand the problem, but to handle redux we must use dispatch. You are not using this, you are just passing redux values. Sorry if I got it wrong, but I use something like this:
import { createTypes } from "reduxsauce";
export const gradesTypes = createTypes(
`
SET_GRADES
RESET_STATE
`,
{ prefix: "GRADES/" }
);
const setGrades = (grades) => {
return (dispatch) => dispatch({ type: gradesTypes.SET_GRADES, grades });
};
const resetState = () => {
return (dispatch) => dispatch({ type: gradesTypes.RESET_STATE });
};
export const gradesActions = {
setGrades,
resetState,
};

Accessing Reducer in container returns undefined

I just wanted to integrate a new Container in my React App, wired it up with Redux and just wanted to see it's all working. It's not however. accessing the reducer via this.props.selection gives me undefined. I don't know why. It does work in other containers, and the reducer has some well-defined initial state. - I'm not sure I see what the difference is here? Am I missing something trivial?
import React, { Component } from 'react'
import { connect } from 'react-redux';
import {bindActionCreators} from 'redux';
export class AudioPlayer extends Component {
constructor(props) {
super(props);
this.state = { someComponentState : true }
}
onLog() {
console.log("Logging:");
console.log(this.props.selection); // gives me: undefined
}
render() {
return (
<div>
<button onClick={()=> this.onLog()}>LOG</button>
</div>
)
}
}
function mapStateToProps (state) {
return {
selection: state.selection
};
}
export default connect(mapStateToProps)(AudioPlayer);
PS: I've simplified this component somewhat, but I think it should still reflect the problem.
edit: reducer example
people have asked to see the reducer, however, I've tried this with several reducers that are already implemented in the app and are working in other containers, so I don't think this is where the problem lies - but who knows:
import { SELECT_ITEM } from '../actions/types';
export default function(state = {}, action) {
switch(action.type) {
case SELECT_ITEM:
return {...state, error:'', selected: true};
}
return state;
}
edit2: mapStateToProps does not seem to be called at all
I just tried to do a console.log in mapStateToProps, to see if it's called, and seems that it never is. Nothing is ever logged. What could be the reason for this?
function mapStateToProps (state) {
console.log("In map function");
console.log(state);
return {
selection: state.selection, //both return
auth: state.auth // undefined
};
}
I also added another reducer (auth) which works elsewhere in the app, but here returns undefined.
edit3: My Root Reducer
import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form';
//reducer imports
import authReducer from './auth_reducer';
import articlesReducer from './articles_reducer';
import userReducer from './user_reducer';
import currentSelectionReducer from './currentSelection_reducer';
const rootReducer = combineReducers({
auth: authReducer,
user: userReducer,
articles: articlesReducer,
selection: currentSelectionReducer,
});
export default rootReducer;
Can you try removing 'export' from 'export class AudioPlayer extends Component'
you can also check this: mapStateToProps not getting called at all
your component code is fine.
In your reducer it should be
export default function(state = { selected: false }, action) {
Further reading:
https://redux.js.org/recipes/structuringreducers/initializingstate
https://stackoverflow.com/a/37823335/2477619
1) In your debugging please check it enters the exact case in the reducer, that it understands the action.type == SELECT_ITEM, and returns the new state.
2) Also notice selection is an object, which contain the 'selected' inside it.
Your 'selection' reducer contains: {...state, error:'', selected: true}
maybe there is a confusion about this?

Array gets undefined - React Redux [duplicate]

This question already has answers here:
JavaScript: Difference between .forEach() and .map()
(17 answers)
Closed 25 days ago.
i am new to React and Redux. Trying to understand the basics and do some simple examples, but i am stuck in this problem for more than one day i can't find the sollution. I imagine that my mistake is a dumb mistake.
The problem is that i can't print the array of users. When debugging, the variable users is loading with all the corrected ids and users, but after executing the <li key={id}>{name}</li> for three times, it comes back to the forEach and gives me this exception: Uncaught TypeError: Cannot read property 'forEach' of undefined, where users is undefined. And i also get an error corresponding to the PropTypes: Invalid prop user of type array supplied to HomePage, expected object
Here is the code:
store/configureStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/index';
const initialState = {};
const middleware = [thunk];
const store = createStore(rootReducer, initialState, compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
));
export default store;
reducers/index.js
import { combineReducers } from 'redux';
import userReducer from './userReducer';
//import groupReducer from './groupReducer';
export default combineReducers({
user: userReducer
});
reducers/userReducer.js
import { GET_USERS, ADD_USER, DELETE_USER } from '../actions/types';
const initialState = {
users: [
{ id: 1, name: 'brunao'},
{ id: 2, name: 'flavio'},
{ id: 3, name: 'dudu'}
]
};
export default function(state = initialState, action) {
switch (action.type) {
case GET_USERS:
return [
...state
];
default:
return state;
}
}
actions/usersAction.js
import { GET_USERS, ADD_USER, DELETE_USER } from './types';
export const getUsers = () => {
return {
type: GET_USERS
};
};
components/HomePage.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getUsers } from '../actions/usersActions';
import PropTypes from 'prop-types';
class HomePage extends Component {
componentDidMount() {
this.props.getUsers();
}
render() {
const { users } = this.props.user;
return(
<div>
<h3>Users</h3>
<ul>
{users.forEach(({id, name}) => (
<li key={id}>{name}</li>
))}
</ul>
</div>
);
}
}
HomePage.propTypes = {
getUsers: PropTypes.func.isRequired,
user: PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
user: state.user
});
export default connect(mapStateToProps, { getUsers })(HomePage);
You are returning a wrong shape of state in your reducer. Your related code:
export default function(state = initialState, action) {
switch (action.type) {
case GET_USERS:
return [
...state
];
default:
return state;
}
}
Here, state is an object but you are returning it in an array by spreading it. So, your state gets broken.
Try it like that:
case GET_USERS:
return state;
As #Idan Dagan pointed out in his answer, actually we do not mutate state in our reducers. I just gave this suggestion since you are just playing around to learn Redux and we are returning the original state here, nothing more. But, this is a suitable and better way to return the state:
case GET_USERS:
return { ...state };
Here is a working code: https://codesandbox.io/s/k5ymxwknpv
I also changed forEach with map again as #Idan Dagan suggested. I haden't realized that. forEach is not the suitable method here since actually it does not return anything. You want to map through your arrays in React and render them.
Also, your state name is confusing :) user.users is a little bit weird, you can think a better one maybe.
Edit after comments
Your GET_USERS action actually is being hit, but you are checking it wrong in your code. You are doing:
export default function(state = initialState, action) {
switch (action.type) {
case GET_USERS:
return Object.assign({}, state);
default:
return {
users: [
{ id: 1, name: "TEST" },
{ id: 2, name: "TEST1" },
{ id: 3, name: "TEST2" }
]
};
}
}
What happens here? First action of Redux is INIT. This is the initialization of your state. Now, since there is no certain action, your reducer hits the default case and returns the TEST one. Now, your state becomes this TEST data. Then your GET_USERS is hit and you return a new object which merges the state which is the TEST one. Here is the steps:
First, state is the `initialState` -> `INIT` runs and hits the default case
State is now TEST one -> GET_USERS hit and returns the `state` which is TEST one
You see the TEST one.
How can you test your actions? Just put a console.log in your reducer:
export default function(state = initialState, action) {
console.log("state",state);
console.log("action",action);
.....
and see GET_USERS actually is being hit. The other option is instead of returning the merged object with state, try to merge it with initialState or with spread operator return a new object by using initialState:
return return Object.assign({}, initialState);
or
return {...initialState}
Last option provides you a little bit more understanding how my first explanation works. Try to return this for your GET_USERS:
return {...state, users:[...state.users, {id:4, name: "foo"}]};
You will see a users list with TEST data but the last one will be your foo. This explains how you loose your initialState if you return anything beside state in your default case.
Last suggestion, you can debug your Redux development with Redux Dev Tools. It is a great tool and does much more than debugging. You can easily track all your operations for Redux.
I'm not sure what are you trying to do with the new state.
But there is couple of changes that you need to do:
Use map instead of forEach (because you want to return a new array).
At the reducer you can return the state like this:
export default function(state = initialState, action) {
switch (action.type) {
case GET_USERS:
return Object.assign({}, state);
default:
return state;
}
}
as mention in redux docs:
We don't mutate the state. We create a copy with Object.assign().

Redux , Getting all the state instead of the state that i passed

I am having a wierd issue in react-redux , i am getting all the state instead of the state that i passed
this is my code:
Action.js
import socketIOClient from 'socket.io-client'
const DATA_URL = "LINK TO API";
export const GET_DATA ='GET_DATA';
export const LIVE_DATA = 'LIVE_DATA';
const parseData= arr => arr.reverse().map((i)=>([i[0],i[1],i[3],i[4],i[2],i[5]]))
export const getData = () => dispatch =>{
fetch(DATA_URL).then(res => res.json())
.then(data => dispatch({
type:GET_DATA,
payload:parseData(data)
}
))
}
export const getLive = () => dispatch => {
var checkTime=0;
const socket = socketIOClient('http://localhost:3001');
socket.on("candle",(res)=>{
if(checkTime <= res[0]){
checkTime = res[0];
dispatch({
type:LIVE_DATA,
payload:res
})
}
})
}
api.js
import {GET_DATA,LIVE_DATA} from '../actions/index';
const INITIAL_STATE = {all:[],live:[]}
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_DATA:
return Object.assign({},state,{all:action.payload})
case LIVE_DATA:
return Object.assign({},state,{live:action.payload})
default:
return state;
}
}
reducer.js
import { combineReducers } from 'redux';
import all from './api';
import live from './api';
const reducers = combineReducers({
candle:all,
livedata:live
});
export default reducers;
As you can see i am passing all to candle and live to livedata
But in my Reduxdevtools i can access everything from both candle and livedata as you can see in the screen shot
This is how i dispatch the action on my component
App.js
const mapStateToProps = state => ({
data: state.candle.all,
live: state.livedata.live
})
Can someone tell me what need to be changed so i could be able to access only
live in the livedata state and all only in the candle state
Thank you
This happened because you're accessing the same reducer but with different names. You should create separate reducer for each.
Like this:
candleReducer.js
import {GET_DATA} from '../actions';
const INITIAL_STATE = { all:[] }
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_DATA:
return Object.assign({},state,{all:action.payload})
default:
return state;
}
}
liveReducer.js
import {LIVE_DATA} from '../actions';
const INITIAL_STATE = { live:[] }
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case LIVE_DATA:
return Object.assign({},state,{ live:action.payload })
default:
return state;
}
}
and then import them into combine reducers:
import { combineReducers } from 'redux';
import all from './candleReducer';
import live from './liveReducer';
const reducers = combineReducers({
candle:all,
livedata:live
});
export default reducers;
You are using the same reducer function for two 'part' of the state.
In your case you are duplicating the same logic in two parts, so the same reducer is called and it react to the same actions and update two entries in the state with the same logic.
You should consider to write two separate reducer for candle and livedata, so each of them will react to a specific action and modify correct entry in the state.
But if candle and livedata are releated to the same domain you should consider to put in one reducer and of course in one section of state, so you will end up in this situation
const reducers = combineReducers({
apiData:liveAndCandleReducer,
});
In apiData you will have
{
all:[],
live: [],
}
It's totally up to you and your application logic.

Categories