React/Redux - Issue iterating json from mapStateToProps - json

I have a React Native app on which I'm trying to apply Redux. It's the first time I try to use Redux, so I think I'm not seeing the elephant in the room.
The problem is that I can't access my props data (generated with mapStateToProps). Here's my code:
reducer.js (in the console log I see the json objects just fine)
const INITIAL_STATE = {
etiquetas: []
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case FETCH_ETIQUETAS_SUCCESS:
//console.log("payload: "+action.payload);
return { ...state, etiquetas: action.payload };
default:
return state;
}
};
component.js (in the console.log I see my data, BUT it seems that it's all in one object now, this is probably what I'm not seeing).
class EtiquetasList extends Component {
componentDidMount() {
this.props.FetchEtiquetas();
}
renderEtiquetas() {
//console.log("etq: "+JSON.stringify(this.props.etiquetas));
if ( this.props.etiquetas.length == 0 ) {
return <ActivityIndicator size="large" color="#00ff00" />
} else {
return this.props.map(etiqueta =>
<EtiquetaDetail key={etiqueta.id} etiqueta={etiqueta} />
);
}
}
render() {
return (
<ScrollView>
{this.renderEtiquetas()}
</ScrollView>
);
}
}
const mapStateToProps = (state) => {
return {
etiquetas: state.etiquetas
};
};
export default connect(mapStateToProps, { FetchEtiquetas })(EtiquetasList);
The map function is for Arrays, not Objects, I know. That's part of my old code.
action.js
import axios from 'axios';
import { FETCH_ETIQUETAS, FETCH_ETIQUETAS_SUCCESS, FETCH_ETIQUETAS_FAILURE } from './types';
const url= 'https://e.dgyd.com.ar/wp-json/wp/v2/etiquetas?_embed&per_page=7';
const fetchSuccess = (dispatch, data)=> {
dispatch({
type: FETCH_ETIQUETAS_SUCCESS,
payload: data
});
}
export function FetchEtiquetas() {
return function (dispatch) {
axios.get( url )
.then(response => {
dispatch({ type: FETCH_ETIQUETAS_SUCCESS, payload: response.data })
} );
}
}
reducers/index.js
import { combineReducers } from 'redux';
import DataReducer from './DataReducer';
export default combineReducers({
etiquetas: DataReducer
});
So, my questions are:
Why is is this always returning undefined?
this.props.etiquetas.length == 0
Why mapStateToProps seems to convert my array of objects into a single object? is this why I have to use JSON.stringify in the console log?
and finally, how do I access my data in the component?
Thank you much in advance!

The problem here, is just the way that you structured your reducer.
const INITIAL_STATE = {
etiquetas: []
};
The code above means that you are creating an object, with a property named "etiquetas" that holds an empty array initially.
In your root reducer file, you import that object, and assign it the name, "etiquetas". So really what your reducer is returning is this:
etiquetas: {
etiquetas: [your array of data]
}
This would explain why you complained about receiving an object. There are two ways to rectify this,
One: Change the mapStateToProps function to this,
const mapStateToProps = (state) => {
return {
etiquetas: state.etiquetas.etiquetas
};
};
Two: Change your reducer to look like this,
export default (state = [], action) => {
switch (action.type) {
case FETCH_ETIQUETAS_SUCCESS:
//console.log("payload: "+action.payload);
return action.payload;
default:
return state;
}
};
This will make sure your reducer returns just an array, not an object with an array inside of it stored in a property. Its up to you to decide which you like better.

Related

Next JS Error serializing `.dehydratedState.queries[0].state.data.config.adapter` returned from `getServerSideProps

I am trying to use react-query to fetch data in getServerSideProps in Next JS but I keep getting this weird error:
Error: Error serializing `.dehydratedState.queries[0].state.data.config.adapter` returned from `getServerSideProps` in "/auth/google/callback".
Reason: `function` cannot be serialized as JSON. Please only return JSON serializable data types.
Here is my code:
// Packages
import { useRouter } from 'next/router'
import { dehydrate, QueryClient, useQuery } from 'react-query';
// APIs
import { completeGoogleAuth } from '../../../hooks/auth';
export async function getServerSideProps(context) {
const queryClient = new QueryClient()
await queryClient.prefetchQuery('completeGoogleAuth', () => completeGoogleAuth(context.query.code));
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
export default function Callback() {
const router = useRouter();
const { data } = useQuery('completeGoogleAuth', () => completeGoogleAuth(router.query.code))
return (
<>
Loading
</>
)
}
I have tried to use JSON.stringify(dehydrate(queryClient)) and also used JSON.parse(JSON.stringify(dehydrate(queryClient))) but none of them worked.
What can I do?
I stumbled across the same error just today, JSON.stringify(dehydrate(queryClient)) or serializing dehydrate(queryClient) by any means won't really work as the object your completeGoogleAuth function is returning has function values in the key-value pairs, here's a picture of the config object.
And as you know, functions can't be JSON serialized as straightforwardly. Now, what I assume you used(or what I did too) for the completeGoogleAuth fetcher function is use Axios as your API client library. I have found that Axios returns objects that can't be JSON serialized. As a solution, I have just used the native JavaScript fetch() API to get API data and the haven't faced any issues since then.
Here's my fetcher function:
export const getScholarshipInfoSSR = async (token) => {
const response = await fetch(
process.env.NEXT_PUBLIC_API_BASE_URL + portalRoutes.getScholarshipInfo,
{
headers: { Authorization: `JWT ${token}` },
}
);
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json().then((data) => ({ data }));
};
Here's the prefetched useQuery invocation:
await queryClient.prefetchQuery("portal", () =>
getScholarshipInfoSSR(token)
);

Can not map over a response json array

I have the Json response from an api in the below format
[
{
id: 1,
class: '10',
section: 'A',
subject: 'Social'
},
{
id: 2,
class: '8',
section: 'C',
subject: 'Social'
},
{
id: 3,
class: '9',
section: 'A',
subject: 'Social'
}
]
I am storing the json response in a state variable and able to print the above json array successfully.
async ListAllTodaysClasses() {
try {
let data = new FormData();
data.append('id', this.state.id)
data.append('year',this.state.year)
data.append('month',this.state.month)
data.append('day', this.state.day)
var url = this.state.url;
console.log(url);
let response = await fetch(url, {
method: 'POST',
body: data
});
let res = await response.json();
this.setState({
subjects: res
})
console.log(this.state.subjects)
} catch(error) {
this.setState({error: error});
console.log("error " + error);
}
}
Here I am trying to loop over an json response array.
this.state.subjects.map((item, key) => (
<TouchableOpacity key={key}>
<View>
{
<Text style={styles.textColor2}>{item.class}th-{item.section}/ {item.subject}</Text>
}
</View>
</TouchableOpacity>
))
But I am getting Typeerror: undefined is not a function
Your question has nothing to do with JSON. You're storing the result of calling response.json(), which parses the JSON in the response and returns the parsed result.
Your code to store it is correct:
this.setState({
subjects: res
})
so I suspect the problem is in your constructor where you set your initial state. The correct initial state for subjects would be like this:
// In your constructor
this.state = {
/*...any other state you have...*/,
subjects: []
};
Notice that subjects is an empty array. I suspect you're setting it to {} (an empty object) or "" (an empty string) or similar. (You're clearly not failing to initialize it, and not using null, since that would produce a different error.)
Side note: Although your setState call is correct, what follows it is not:
this.setState({
subjects: res
})
console.log(this.state.subjects) // <=== Will not see the updated state
Remember that state updates are asynchronous.
You probably need to init your state.subjects:
class YourClassName extends React.Component {
constructor() {
this.state = {
subjects: []
}
}
}

How to efficiently fetch data from URL and read it with reactjs?

I have some URL with json and need to read data.
For the sake of this example json looks like this:
{
"results": [
...
],
"info": {
...
}
}
I want to return fetched data as a property of a component.
What is the best way to do it?
I tried to do that with axios. I managed to fetch data, but after setState in render() method I received an empty object. This is the code:
export default class MainPage extends React.Component {
constructor(props: any) {
super(props);
this.state = {
list: {},
};
}
public componentWillMount() {
axios.get(someURL)
.then( (response) => {
this.setState({list: response.data});
})
.catch( (error) => {
console.log("FAILED", error);
});
}
public render(): JSX.Element {
const {list}: any = this.state;
const data: IScheduler = list;
console.log(data); // empty state object
return (
<div className="main-page-container">
<MyTable data={data}/> // cannot return data
</div>
);
}
}
I don't have a clue why in render() method the data has gone. If I put
console.log(response.data);
in .then section, I get the data with status 200.
So I ask now if there is the other way to do that.
I would be grateful for any help.
----Updated----
In MyTable component I got an error after this:
const flightIndex: number
= data.results.findIndex((f) => f.name === result);
Error is:
Uncaught TypeError: Cannot read property 'findIndex' of undefined
What's wrong here? How to tell react this is not a property?
Before the request is returned, React will try to render your component. Then once the request is completed and the data is returned, react will re-render your component following the setState call.
The problem is that your code does not account for an empty/undefined data object. Just add a check, i.e.
if (data && data.results) {
data.results.findIndex(...);
} else {
// display some loading message
}
In React, after you have stored your ajax result in the state of the component (which you do appear to be doing), you can retrieve that result by calling this.state.list
So to make sure this is working properly, try <MyTable data={this.state.list}>
https://daveceddia.com/ajax-requests-in-react/

receive data from react object, using redux

I'm trying to parse a json received from external api.
My reducer is:
import { RECEIVED_FORECAST } from '../actions/index';
export default function ForecastReducer (state = [], action) {
switch (action.type) {
case RECEIVED_FORECAST:
return Object.assign({}, state, {
item: action.forecast
})
default:
return state;
}
}
Then main reducer goes like:
import { combineReducers } from 'redux';
import ForecastReducer from './forecast_reducer';
const rootReducer = combineReducers({
forecast: ForecastReducer
});
export default rootReducer;
and container looks like
import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
class WeatherResult extends Component {
render() {
const forecast = this.props.forecast.item;
{console.log('almost: ', forecast)}
return (
<div>
<h1> </h1>
</div>
)
}
}
function mapStateToProps({ forecast }) {
return {
forecast
}
}
export default connect(mapStateToProps)(WeatherResult)
Output of the almost is exactly the same son as I supposed:
almost:
Object
currently: {time: 1476406181, summary: "Drizzle", icon: "rain", nearestStormDistance: 0, precipIntensity: 0.0048, …}
daily: {summary: "Light rain on Saturday and Thursday, with temperatures rising to 92°F on Wednesday.", icon: "rain", data: Array}
So, my question is, how can I show the value of, let's say forecast.currently.summary?
1) If I just try to insert it within {} I receive : 'TypeError: undefined is not an object (evaluating 'forecast.currently')'
2) I can't use mapping as the json might have other components added
Is there any method to get to this property directly, without mapping all the file?
Thanks
The problem you have is that you're requesting the data. That doesn't complete immediately. Think about what the app is doing while you're waiting for the weather data to arrive.
It's displaying something. In your case, the render method is failing because you're trying to show data that hasn't arrived yet.
The solution:
render() {
const forecast = this.props.forecast;
const text = forecast && forecast.item.currently.summary || 'loading...';
return (
<div>
<h1>{text}</h1>
</div>
)
}
}
This way you check if you already have the data and if not, you show something useful.

How do I deal with d3.json to get my InitialState in React/redux

Using React/Redux, I'm trying to get external data into my initial state with express. I'm used to work with D3 so I wanted to use d3.json with my reducer like this :
var url = 'http://localhost:3000/authors';
function cool() {
d3.json(url, function(data) {
dataset = data;
});
}
const authorData = (state = dataset, action) => {
switch (action.type) {
case 'DATA_CHART_ALL':
return action.data
case 'DATA_CHART_FILTER':
return action.data
default:
return state;
}
};
export default authorData;
Since D3.json is a callback function, my reducer is returned undefined. How do I deal with this ? Can I use something else than d3.json ?
You need a function that will act as a dispatcher in redux:
// redux action using a dispatcher (think middleware)
export function cool(url) {
return function(dispatch) {
return d3.json(url, response => {
dispatch(setData(response))
}
}
}
// redux action
export function setData(data) {
return {
type: 'DATA_CHART_ALL',
data
}
}
const authorDataReducer = (state = {}, action) => {
switch (action.type) {
case 'DATA_CHART_ALL':
return action.data
case 'DATA_CHART_FILTER':
return action.data
default:
return state;
}
};
export authorDataReducer;
to use it:
call this at the beginning of your application:
store.dispatch(cool("MY_URL"));
Note that i'm not checking for error handling in the request