React/Redux with Google Sheet API v4 is not fetching data - javascript

I've been making an app that is using the Google Sheet API and React/Redux.
If I hit the API from the component itself it works but I'm having an issue when it comes to fetch data through Redux.
This is code
Action creator:
export function fetchList() {
let data = null;
gapi.client.sheets.spreadsheets.values.get({
spreadsheetId: FULL_LIST_ID,
range: RANGE
}).then((response) => {
data = response.result.values;
}, (response) => {
throw response.result.error.message;
});
return {
type: FETCH_LIST,
payload: data
}
}
Reducer:
export default function(state = INITIAL_STATE, action = {} ) {
switch (action.type) {
case FETCH_LIST:
return { ...state, list: action.payload };
default:
return state;
}
}
Component:
import React from 'react';
import { connect } from 'react-redux';
import { fetchList } from '../../actions/index.jsx';
export class DropdownList extends React.Component {
constructor(props) {
super(props);
this.state = { res: null }
// this._fetchList = this._fetchList.bind(this);
}
componentWillMount() {
// this should fetch the data from Redux
this.props.fetchList();
// so that when
console.log(this.props);
// I should see the values attached to the payload
// instead this is fetching the data from the API hit here
this._fetchList();
}
// Here I'm hitting the API from the component
_fetchList() {
gapi.client.sheets.spreadsheets.values.get({
spreadsheetId: FULL_LIST_ID,
range: ['LIST!A1:B']
}).then((response) => {
this.setState({ res: response.result.values });
}, (response) => {
throw response.result.error.message;
});
}
_renderList() {
// this uses the values fetched locally
// return this.state.res.map((val, index) => {});
}
render() {
if (!this.state.res) {
return <div>Loading...</div>;
}
return (
<div>
{this._renderList()}
</div>
);
}
}
function mapStateToProps(state) {
return { list: state.list }
}
export default connect(mapStateToProps, { fetchList })(DropdownList);
Does anybody can help me out?
Thanks

OK, solved!
It was an issue of sync so I needed to use Redux-Thunk as a middleware in my Action Creator.

Related

Updating states from input field when using React-Redux

I am currently using React-Redux but for a pretty simple app.
The app just simply takes a user ID, password, and an address of a server that the user wants to get into. It gets into the server and runs a script in the server. But the functionality of the app is not important in my question.
I only need 3 states (username, password, and server_address) for the app.
However, I have three different reducers and actions that do the same thing just with the different state.
For example,
userReducer.js
// reducer functions takes a default state and an action to apply
import { UPDATE_USER } from '../actions/userActions'
export default function userReducer(state = '', { type, payload }) {
switch (type) {
case UPDATE_USER:
return payload;
default:
return state;
}
}
passwordReducer.js
// reducer functions takes a default state and an action to apply
import { UPDATE_PASSWORD } from '../actions/passwordActions'
export default function passwordReducer(state = '', { type, payload }) {
switch (type) {
case UPDATE_PASSWORD:
return payload;
default:
return state;
}
}
routerReducer.js // this is the server
// reducer functions takes a default state and an action to apply
import { UPDATE_ROUTER } from '../actions/routerActions'
export default function routerReducer(state = '', { type, payload }) {
switch (type) {
case UPDATE_ROUTER:
return payload;
default:
return state;
}
}
and actions that look like this:
export const UPDATE_PASSWORD = 'updatePassword'
export function updatePassword(newPassword) {
return {
type: UPDATE_PASSWORD,
payload: {
'newPassword': newPassword
}
}
}
It's same for the other two with the different variable.
Then in my component, I just connected mapActionsToProps to the component and put 3 functions that does the same thing (updating the state)
class Container extends React.Component {
constructor(props) {
super(props)
}
onUpdateUser = (e) => {
this.props.onUpdateUser(e.target.value)
}
onUpdatePassword = (e) => {
this.props.onUpdatePassword(e.target.value)
}
onUpdateRouter = (e) => {
this.props.onUpdateRouter(e.target.value)
}
...
using it like
This kinda works, but I am not sure if this is the right way to use React-Redux. First of all, they are duplicates and do not seem like a good practice. However, I can't think of a way to update each state in a React-Redux way without just putting similar codes.
Any help?
You could pass the event to your action.js
export const onInputChange = event => ({
type: 'UPDATE_INPUT',
payload: event
});
And simply grab the name and the value of the event in your reducer.js
const INITIAL_STATE = {
user: '',
password: ''
}
export const inputReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case: 'UPDATE_INPUT':
return { ...state, [action.payload.target.name]: action.payload.target.value };
default:
return state;
};
};
Then in your component
// ...
handleChange(event) {
this.props.onInputChange(event);
};
// ...
<input type='text' name='user' onChange={this.handleChange} />
You can use a single function just to send the key/value pairs you want to update.
export const UPDATE_USER_VALUE = 'updateUserValues'
export function updateUser(payload) {
return {
type: UPDATE_USER_VALUE,
payload: payload,
}
}
You must call that function like this:
onUpdateUser = (e) => {
this.props.onUpdateUser({
key: 'name',
value: e.target.value
})
}
onUpdatePassword = (e) => {
this.props.onUpdateUser({
key: 'password',
value: e.target.value
})
}
Then just update the values.
import { UPDATE_USER_VALUE } from '../actions/userActions'
const defaultState = {
username = '',
password = '',
server_address = ''
};
export default function passwordReducer(state = defaultState, { type, payload }) {
switch (type) {
case UPDATE_USER_VALUE:
return {
...state,
state[payload.key]: payload.value
};
default:
return state;
}
}

React Redux API call, data not making it back to component

In the last couple of days I have been working on my Redux api call. I am actually having a problem getting the data back to the view component. Currently I'm able to see the data in the in the action generator, so I know at least I'm able to get it. However, nothing is showing in the view. I imagine it may have something to do with when it's loading. This is why I tried to load it when the component is rendering.
https://djangoandreact.herokuapp.com/user/1 is what is not loading.
codesandbox: https://codesandbox.io/s/zlor60q3jm?from-embed
Should be able to go to /user/1 at the end similar to going to /1 brings up an article(Tough Hope)
Heres the view component:
import React from "react";
import { connect } from "react-redux";
import { fetchUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
const userID = this.props.match.params.userID;
fetchUser(userID); //fixed
}
render() {
const { user } = this.props.user;
console.log(user);
return (
<div>
<h3>{user.username}</h3>
</div>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
const mapDispatchToProps = (dispatch, ownProps) => ({
fetchUser: dispatch(fetchUser(ownProps.match.params.userID))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserDetailView);
Action generator
import axios from "axios";
import { thunk } from "react-redux";
export function fetchUser(userID) {
console.log(userID);
return dispatch => {
return axios.get(`/api/user/${userID}`).then(res => {
dispatch(fetchUserSuccess(res.data));
console.log(res.data); // loads data
});
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const FETCH_USER_BEGIN = "FETCH_USER_BEGIN";
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";
export const fetchUserBegin = () => ({
type: FETCH_USER_BEGIN
});
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: { user }
});
export const fetchUserFailure = error => ({
type: FETCH_USER_FAILURE,
payload: { error }
});
Reducers(which are probably fine):
import {
FETCH_USER_BEGIN,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE
} from "../actions/actionTypes";
const initialState = {
user: {},
loading: false,
error: null
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case FETCH_USER_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
case FETCH_USER_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
return state;
}
}
folks. I found it.
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
user is supposed to be user:action.payload
Also, the user action was supposed to be
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: user
})
WOOOOW. But, honestly, I learned so much about Redux in the last two sleepless nights, it was worth the pain. Really was. Now, instead of copy pasta, I know what an action generator is and does, and reducer (obvi)

Publish and subscribe events in React Native

Is it possible to publish and subscribe to events(like in ionic) for Component communication. The two components I have there are no related (there are not parent and child).
One component is a header that has a button Publish , and the other component is a form. What i want is to send an event from the clicked button to the form for validation purpose that says for example the field body cant be empty something like that.
EDIT:
I am using router flux. The component i have the form is NewPost and the one with the Button publish is ButtonsNewPost. Are this components parent and child? They can comunicate somehow?
<Scene
key="newPost"
component={NewPost}
hideNavBar={false}
renderRightButton={<ButtonsNewPost/>}
navBarButtonColor='#fff'
>
SOLUTION:
newPost.js
componentWillReceiveProps(newProps) {
let validationMessage;
if(newProps.validationBody) {
validationMessage = 'El campo descripción es requerido';
this.showToastValidation(validationMessage);
//without the next line the validation toast only appear once
this.props.validation_body(false);
}
}
const mapStateToProps = state => {
return {
validationBody: state.validationBody
}
}
const mapDispatchToProps = dispatch => {
return {
validation_body: (validationBody) =>
dispatch(validation_body(validationBody))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NewPost)
reducers/validationBody.js
export default (state = false, action) => {
switch(action.type) {
case 'validation_body':
return action.payload
default:
return state
}
}
reducers/index.js
import validationBody from './validationBody';
export default combineReducers({
validationBody: validationBody
})
actions/index.js
export const validation_body = (validationBody) => {
return {
type: 'validation_body',
payload: validationBody
}
}
buttonsNewPost.js
if (!window.description) {
this.props.validation_body(true);
return;
}
const mapDispatchToProps = dispatch => {
return {
validation_body: (validationBody) =>
dispatch(validation_body(validationBody)),
}
}
export default connect(null, mapDispatchToProps)(ButtonsNewPost)
You can use the react-native-event-listeners library:
https://github.com/meinto/react-native-event-listeners
Usage is similar to Ionic events:
import { EventRegister } from 'react-native-event-listeners'
/*
* RECEIVER COMPONENT
*/
class Receiver extends PureComponent {
constructor(props) {
super(props)
this.state = {
data: 'no data',
}
}
componentWillMount() {
this.listener = EventRegister.addEventListener('myCustomEvent', (data) => {
this.setState({
data,
})
})
}
componentWillUnmount() {
EventRegister.removeEventListener(this.listener)
}
render() {
return <Text>{this.state.data}</Text>
}
}
/*
* SENDER COMPONENT
*/
const Sender = (props) => (
<TouchableHighlight
onPress={() => {
EventRegister.emit('myCustomEvent', 'it works!!!')
})
><Text>Send Event</Text></TouchableHighlight>
)

Able to view JSON in console but don't know how to display in view - React-Redux

I am still learning React-Redux. I understand how to retrieve simple JSON arrays. However, I am not sure how to call a nested object. I am trying to grab the title and am viewing this in the console:
Object
data
:
Object
data
:
Object
data
:
Object
after
:
"t3_5t0hy2"
before
:
null
children
:
Array[25]
0
:
Object
data
:
Object
title
:
"The Google Analytics Setup I Use on Every Site I Build (by Philip Walton)"
dataAction.js
import axios from 'axios';
export function fetchData(){
return function(dispatch){
axios.get("https://www.reddit.com/r/webdev/top/.json")
.then((response) => {
dispatch({ type: "FETCH_DATA_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_DATA_REJECTED", payload: err})
})
}
}
export function addData(id, text){
return {
type: 'ADD_DATA',
payload:{
id,
title,
},
}
}
export function updateData(id, text){
return {
type: 'UPDATE_DATA',
payload: {
id,
title,
},
}
}
export function deleteData(id){
return {
type: 'DELETE_DATA',
payload: id
}
}
Layout.js (component)
import React from "react"
import { connect } from "react-redux"
import { fetchUser } from "../actions/userActions"
import { fetchPartner } from "../actions/projectActions"
import { fetchData } from "../actions/dataActions"
#connect((store) => {
return {
user: store.user.user,
userFetched: store.user.fetched,
partner: store.partner.partner,
partnerFetched: store.partner.fetched,
data: store.data.data
};
})
export default class Layout extends React.Component {
componentWillMount() {
this.props.dispatch(fetchUser())
this.props.dispatch(fetchPartner())
this.props.dispatch(fetchData())
}
render() {
const { user, partner, data } = this.props;
//const mappedData = data.map(data => <li>{data.title}</li>)
return <div>
<h1>{user.name}{user.age}</h1>
<h1>{partner.title}</h1>
<ul>{data.title}</ul>
</div>
}
}
Reducer.js
export default function reducer(state={
data: {
data: {}
},
fetching: false,
fetched: false,
error: null,
}, action) {
switch(action.type){
case "FETCH_DATA":{
return {...state, fetching:true}
}
case "FETCH_DATA_REJECTED":{
return {...state, fetching: false, error: action.payload}
}
case "FETCH_DATA_FULFILLED":{
return {...state, fetching: false, fetched: true, data: action.payload}
}
case "ADD_DATA":{
return {...state, data: [...state.data, action.payload]}
}
case "UPDATE_DATA":{
const { id, title } = action.payload
const newData = [...state.data]
const dataToUpdate = newData.findIndex(data => data.id === id)
newData[dataToUpdate] = action.payload;
return {...state, data: newData}
}
case "DELETE_DATA":{
return {...state, data: state.data.filter(data => data.id !== action.payload)}
}
}
return state
}
When this issue is solved, the next step would be to iterate through the object, which I'm also not sure how to achieve.
As you are sending payload: response.data You can go further in the object structure and send the actual data in payload.
Once you send the payload you would need a reducer which will change the state. Follow this tutorial on how to create reducer.
http://blog.scottlogic.com/2016/05/19/redux-reducer-arrays.html
Then once the state is updated, you will have the code reading the values from state. Once the state change the React will automatically update or render and you can write your logic.

How to handle multiple loads with redux-observable

I'm having trouble loading multiple data with redux and redux-observable.
I need to load a Serie object, through ajax, that contains a list of questions and for each question I need to fetch it's images, audios and videos blobs, through ajax too.
As for now I am able to do this, but when I try to display my image in React, it doesn't work. I'm guessing that it sends the Serie object before having the images loaded, therefore it doesn't update the view once the blob retrieved. In fact if I add a delay in my Observable between the two maps, the images appear in the view.
I'm new to redux-observable (RxJS) and just trying to make it work even if it's not useful in my case.
Here is my action file.
import { Observable } from 'rxjs';
import { GET_SERIE, FETCH_SERIE } from './types';
import { ROOT_URL, SETTINGS } from './settings';
export function getSerie() {
return { type: GET_SERIE }
}
function getBlob(assets) {
return assets.map(asset => {
fetch(asset.url_lg ? asset.url_lg : asset.url)
.then(response => response.blob())
.then(blob => asset['blob'] = URL.createObjectURL(blob))
return asset;
});
}
function getAssets(serie) {
serie.questions.forEach(question => {
question.pictures = getBlob(question.pictures);
question.audios = getBlob(question.audios);
question.videos = getBlob(question.videos);
});
return serie;
}
function setSerie(serie) {
return {
type: FETCH_SERIE,
serie
}
}
const fetchSerie = () =>
fetch(`${ROOT_URL}/free_serie`, SETTINGS)
.then((response) => response.json());
export const fetchSerieEpic = (action$) =>
action$.ofType(GET_SERIE)
.mergeMap((action) =>
Observable.from(fetchSerie())
.map(({ data }) => getAssets(data.serie))
.map( serie => setSerie(serie))
);
The reducer
import * as types from '../actions/types';
const initialState = {
serie: null
};
export default function(state = initialState, action) {
switch (action.type) {
case types.FETCH_SERIE:
return setSerie(state, action);
default:
return state;
}
}
function setSerie(state, action) {
const { serie } = action;
return { ...state, serie };
}
And the view that dispatches the event
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actions from '../actions';
class App extends Component {
componentWillMount() {
this.props.fetchSerie();
}
render() {
if(this.props.serie !== null) {
return(
<div>{this.props.children}</div>
);
}
return <div>Loading ...</div>;
}
}
function mapStateToProps(state) {
const { serie } = state;
return serie;
}
function mapDispatchToProps(dispatch) {
return {
fetchSerie: bindActionCreators(actions.getSerie, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
I've been looking multiple articles about redux and redux-observable, but found nothing that could help me.

Categories