Async API Call, Redux React-Native - javascript

I have recently started to learn React-Native and I am trying to implement Redux to manage the state of my app.
As I have experience with React JS I implemented the state manage Redux the same way I would usually do with React JS. Everything seems to work except the async api call. The props in store change but it does not change in component props.
Here is how I set my redux store
import { createStore, compose, applyMiddleware } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
import reducer from './reducers';
//create initial state
const initialState = {};
//create a variable that holds all the middleware
const middleware = [thunk, logger];
//create the store
const store = createStore(
reducer,
initialState,
compose(
applyMiddleware(...middleware)
)
);
export default store;
My reducer component
import { FETCH_REQUEST_BEGIN, FETCH_REQUEST_FAILED, DATA_FETCHED} from '../constants';
const initialState = {
movieResults: null,
fetching : false,
failed : false
}
export default (state = initialState, actions)=>{
switch(actions.type){
case FETCH_REQUEST_BEGIN:
return {
...state,
fetching : true
};
case DATA_FETCHED :
return {
...state,
movieResults : actions.payload,
fetchin : false
}
case FETCH_REQUEST_FAILED:
return {
...state,
failed : true
};
default :
return state;
}
}
This is the root reducer
import { combineReducers } from 'redux';
import movieReducer from './movieReducer';
export default combineReducers({
movie : movieReducer
});
action component
import axios from 'axios';
import { FETCH_REQUEST_BEGIN, FETCH_REQUEST_FAILED } from '../constants';
const apiRequest = (url, type) => {
return dispatch => {
dispatch({
type : FETCH_REQUEST_BEGIN
});
axios.get(url)
.then((results) => {
dispatch({
type : type,
payload : results.data
});
}).catch((error) => {
dispatch({
type : FETCH_REQUEST_FAILED,
payload : error
});
});
}
}
export default apiRequest;
Main component
import React, { Component } from 'react';
import { View, Text, Button, ActivityIndicator } from 'react-native';
import { API, DATA_FETCHED } from '../constants';
import { apiRequest } from '../actions';
import { connect } from 'react-redux';
class Home extends Component {
componentWillMount() {
const discoverUrlMovies =`https://jsonplaceholder.typicode.com/posts`;
this.fetchData(discoverUrlMovies, DATA_FETCHED);
}
fetchData = (url, type) => {
this.props.apiRequest(url, type);
}
shouldComponentUpdate(nextProps, prevProps){
return nextProps != prevProps
}
render() {
const { movie } = this.props;
//console out the props
console.log('this.props', this.props);
let displayMovies;
if(movie === undefined || movie === null ){
displayMovies = <ActivityIndicator size = 'large' color = '#121222'/>
}else {
displayMovies = <Text>Working</Text>
}
return (
<View style = {{flex: 1}}>
<Text>Home</Text>
{
//display result
displayMovies
}
</View>
);
}
}
const mapStateToProps = (state) => {
return {
movie : state.movieResults
}
}
export default connect(mapStateToProps, { apiRequest })(Home);
What am I missing / doing wrong?

You need to define your mapStateToProps func as
movie : state.movie.movieResults
since you're combining them as
export default combineReducers({
movie : movieReducer
});

Related

Redux and Axios get. method not returning any data

Need some help.
As I am trying to get some understanding of React/REdux global state I made some simple get request.
This is done with Axios, thunk, Redux, but i can't get this working
I have Post.js file, nothing fancy
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import PostForm from './PostForm';
export class Post extends Component {
static propTypes = {
posts: PropTypes.any,
fetchPosts: PropTypes.func,
};
componentDidMount() {
const { fetchPosts } = this.props;
fetchPosts();
}
render() {
const { posts } = this.props;
return (
<div>
<PostForm addPost={this.onSubmit} />
<br />
<div>
{posts.map(post => (
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
))}
</div>
</div>
);
}
}
export default Post;
Next i have my PostContainer.js
import { connect } from 'react-redux';
import Post from './Post';
import { fetchFromApi } from '../reducers/postReducers';
const mapStateToProps = state => ({
posts: state.posts,
});
const mapDispatchToProps = dispatch => ({
fetchPosts: () => dispatch(fetchFromApi()),
});
export default connect(mapStateToProps, mapDispatchToProps)(Post);
My reducer
import Axios from 'axios';
/* action type */
const FETCH_POSTS = 'FETCH_POSTS';
/* action creator */
export const fetchStarted = payload => ({ payload, type: FETCH_POSTS });
/* thunk */
export const fetchFromApi = () => {
return (dispatch, getState) => {
Axios.get('https://jsonplaceholder.typicode.com/posts?_limit=5').then(res =>
dispatch(fetchStarted(res.data))
);
};
};
/* reducer */
export default function reducer(state = [], action = {}) {
switch (action.type) {
case FETCH_POSTS: {
return {
...state,
data: action.payload,
};
}
default:
return state;
}
}
and my store
import { combineReducers, applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import postReducer from './reducers/postReducers';
const initialState = {
posts: {
data: {},
},
};
const reducers = {
posts: postReducer,
};
Object.keys(initialState).forEach(item => {
if (typeof reducers[item] == 'undefined') {
reducers[item] = (state = null) => state;
}
});
const combinedReducers = combineReducers(reducers);
const store = createStore(
combinedReducers,
initialState,
composeWithDevTools(applyMiddleware(thunk))
);
export default store;
All of that is doing not much. My map method is trying to map empty posts object. And for some reason my fetchPosts is not dispatched. I have reade some old posts here but still can't get this working
Thanks
Edit
this is my app.js file with container
import React from 'react';
import './App.css';
import Post from './components/PostContainer';
import { Provider } from 'react-redux';
import store from './store';
function App() {
return (
<Provider store={store}>
<div className='App'>
<Post />
</div>
</Provider>
);
}
export default App;
I managed to get this working.
Data was not there when my posts array was render. After passing simple if statemante all is working

Component is not updating (react-redux)

i'm another guy with the same question; Sorry, but I could not see yet my error.I'm suspecting is something about mutation, but I tried (almost) everything, getting mad. My component is not updating when I add stuff to my store, it seems is rendering just for once (first time)
I was trying with lifecycles but nothing
ACTION
export default async function getJoke() {
let jk = await axios.get('https://jokeapi.p.rapidapi.com/category/any' ,{ headers :{
'x-rapidapi-host' : 'jokeapi.p.rapidapi.com',
'x-rapidapi-key' : "***********************" }} );
let joke;
if(jk.data.type === 'single') {
joke = jk.data.joke
} else {
joke = { setup : jk.data.setup,
delivery : jk.data.delivery}
}
let getjoke = {
type : 'GETJOKE',
joke
}
store.dispatch(getjoke) ;
}
REDUCER
let initialState = { jokes : [ ] };
function joke (state = initialState, action) {
switch (action.type) {
case 'GETJOKE' : return {...state,
jokes :[...state.jokes, {jokes: action.joke}]
} ;
default : return state;
}
}
export default joke;
REDUCER-INDEX
import { combineReducers } from 'redux';
import joke from './joke'
export default combineReducers ({
joke
})
STORE
import { createStore } from 'redux';
import Combined from './reducer';
export default createStore(Combined);
COMPONENT
import React from 'react';
import BotJoke from './btnJoke';
import {connect} from 'react-redux';
import store from '../store'
class joke extends React.Component {
constructor(props) {
super(props);
this.state = {
jokes : props.jokes
}
console.log('MENDA',this.state.jokes);
}
componentDidMount() {
console.log('willdisssd receive props');
/* this.setState({jokes : [...this.state.jokes, nextProps]})
*/
}
componentWillMount(){
console.log('will mount',this.state.jokes);
console.log('Entra');
console.log('NEXT PROPS');
}
render() {
return (
<div>
<BotJoke/>
{ this.state.jokes !== undefined ? <h1>Hay bromas</h1>: <h1>NO HAY BROMAS</h1> }
</div>
)
} }
function mapStateToProps (state) {
console.log('STorE', store.getState());
return {
jokes : state.jokes
}
}
let connection = connect(mapStateToProps)(joke);
export default connection;
What you are doing is an anti pattern, using the props to setState though you can just use props directly instead.
Use this.props.jokes everywhere in your component, it should work. Let me know.
EDIT:
Assuming you want the jokes to be an array:
REDUCER
let initialState = { jokes : [ ] };
function joke (state = initialState, action) {
switch (action.type) {
case 'GETJOKE' : return {...state,
jokes :[...state.jokes, action.joke]
} ;
default : return state;
}
}
export default joke;
REDUCER-INDEX
import { combineReducers } from 'redux';
import joke from './joke'
export default combineReducers ({
joke
})
STORE
import { createStore } from 'redux';
import Combined from './reducer';
export default createStore(Combined);
COMPONENT
import React from 'react';
import BotJoke from './btnJoke';
import {connect} from 'react-redux';
import store from '../store'
const joke = (props) => {
const length = props.jokes.length
return (
<div>
<BotJoke/>
{ length !== 0 ? <h1>{JSON.stringify(props.jokes[length-1])}</h1>: <h1>NO HAY BROMAS</h1> }
</div>
)
}
function mapStateToProps (state) {
console.log('STorE', store.getState());
return {
jokes : state.jokes
}
}
let connection = connect(mapStateToProps)(joke);
export default connection;

Redux-thunk dispatch function doesn't work on Laravel

I'm using React-Laravel for my project.
The problem is when I tried to use redux-thunk for the asynchronous dispatch function.
My dispatch function won't get executed.
Please do help me figure out this problem.
I have already tried to use promise or redux-devtools-extension library
https://codeburst.io/reactjs-app-with-laravel-restful-api-endpoint-part-2-aef12fe6db02
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import Layout from './jsx/Layout/Layout';
import marketplaceReducer from './store/reducers/marketplace';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const appReducer = combineReducers({
marketplace: marketplaceReducer
});
const rootReducer = (state, action) => {
return appReducer(state, action);
}
const store = createStore(rootReducer, composeEnhancers(
applyMiddleware(logger, thunk)
));
const render = (
<Provider store={store}>
<BrowserRouter>
<Layout />
</BrowserRouter>
</Provider>
);
ReactDOM.render(render, document.getElementById('root'));
marketplace.js (action)
import * as actionTypes from './actionTypes';
import axios from '../../axios';
export const loadMarketplace = () => {
console.log("Load Marketplace");
return {
type: actionTypes.LOAD_MARKETPLACE
};
}
export const successMarketplace = (data) => {
console.log("Success Marketplace");
return {
type: actionTypes.SUCCESS_MARKETPLACE,
data: data
}
}
export const failedMarketplace = () => {
console.log("Failed Marketplace");
return {
type: actionTypes.FAILED_MARKETPLACE
}
}
export const showMarketplace = () => {
console.log("Show Marketplace Action")
return dispatch => {
//This is the problem
//Inside this function, I can't see any console.log, even loadMarketplace() didn't get called.
console.log("Show Marketplace in dispatch");
dispatch(loadMarketplace());
axios.get('/marketplaces')
.then(response => {
dispatch(successMarketplace(response));
})
.catch(error => {
dispatch(failedMarketplace());
});
};
}
marketplace.js (reducer)
import * as actionTypes from '../actions/actionTypes';
const initial_state = {
data: [],
loading: false
}
const loadMarketplace = (state, action) => {
console.log("Load Marketplace Reducer")
return {
...state,
loading: true
};
}
const successMarketplace = (state, action) => {
console.log("Success Marketplace Reducer", action.data)
return {
...state,
loading: false,
data: action.data
};
}
const failedMarketplace = (state, action) => {
return {
...state,
loading: false
};
}
const reducer = (state = initial_state, action) => {
//This is called when the first init, never got it through showMarketplace() function.
console.log("Marketplace Reducer", action);
switch (action.type) {
case actionTypes.LOAD_MARKETPLACE: return loadMarketplace(state, action);
case actionTypes.SUCCESS_MARKETPLACE: return successMarketplace(state, action);
case actionTypes.FAILED_MARKETPLACE: return failedMarketplace(state, action);
default: return state;
}
}
export default reducer;
Marketplace.js (jsx view)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions';
class Marketplace extends Component {
componentDidMount() {
console.log('[ComponentDidMount] Marketplace')
this.props.showMarketplace();
}
render() {
return (
<React.Fragment>
Marketplace
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => {
return {
showMarketplace: () => dispatch(actions.showMarketplace)
};
}
export default connect(null, mapDispatchToProps)(Marketplace);
This is the result of my console.log (when loading the first time for Marketplace.js)
Please do help, I've been struggling for 2 hours or more, only because of this problem. (This is my first time using React-Laravel).
Thank you.
I already found the problem. It is not redux-thunk problem.
It is actually a normal Redux problem we found anywhere.
Marketplace.js (jsx view)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions';
class Marketplace extends Component {
componentDidMount() {
console.log('[ComponentDidMount] Marketplace')
this.props.showMarketplace();
}
render() {
return (
<React.Fragment>
Marketplace
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => {
return {
showMarketplace: () => dispatch(actions.showMarketplace) //THIS IS THE PROBLEM, IT IS NOT EXECUTING PROPERLY. THIS ONE SHOULD BE
showMarketplace: () => dispatch(actions.showMarketplace()) //SHOULD BE LIKE THIS.
};
}
export default connect(null, mapDispatchToProps)(Marketplace);
Edited: I think it is something about thunk is not added right to redux.
First of all try to add only thunk.
const store = createStore(rootReducer, composeEnhancers(
applyMiddleware(thunk)
));
If it works, maybe try to change the order of them.

Redux on React Native not able to render from reducer

I am new to redux and it might be some silly error. I am trying to make an Api call in Action and pass the data to the Reducer. I can see the data passed correctly in the reducer with action.data. I think the problem is in mapStateToProps in the component therefore I am not able to pass the state and render the component. Please find below action - reducers - store.js - home.js
ACTION.JS
export const DATA_AVAILABLE = 'DATA_AVAILABLE';
export function getData(){
return (dispatch) => {
//Make API Call
fetch("MY API URL").then((response) => {
return response.json();
}).then((data) => {
var data = data.articles;
console.log(data)
dispatch({type: DATA_AVAILABLE, data:data});
})
};
}
this is Reducers.JS
import { combineReducers } from 'redux';
import { DATA_AVAILABLE } from "../actions/" //Import the actions types constant we defined in our actions
let dataState = {
data: [],
loading:true
};
const dataReducer = (state = dataState, action) => {
switch (action.type) {
case DATA_AVAILABLE:
state = Object.assign({}, state, {
data: [
...action.data //update current state data reference
],
loading: false
});
console.log(action.data);
return state;
default:
return state;
}
};
// Combine all the reducers
const rootReducer = combineReducers({
dataReducer
// ,[ANOTHER REDUCER], [ANOTHER REDUCER] ....
})
export default rootReducer;
this is Store.js with Redux-thunk
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../app/reducers/index'; //Import the reducer
// Connect our store to the reducers
export default createStore(reducers, applyMiddleware(thunk));
and finally home.js component when I need to pass the new state and render it
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
FlatList,
View,
Text,
ActivityIndicator
} from 'react-native';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions'; //Import your actions
class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
this.props.getData(); //call our action
}
render() {
if (this.props.loading) {
return (
<View style={styles.activityIndicatorContainer}>
<ActivityIndicator animating={true}/>
</View>
);
} else {
console.log(this.state)
return (
<View style={styles.row}>
<Text style={styles.title}>
{this.props.data}
fomrmo
</Text>
<Text style={styles.description}>
</Text>
</View>
);
}
}
};
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.dataReducer.loading,
data: state.dataReducer.data
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Assuming that:
case DATA_AVAILABLE:
console.log(action.data.length)
will console.log something more than 0
change your reducer action:
const dataReducer = (state = dataState, action) => {
switch (action.type) {
case DATA_AVAILABLE:
return {
...state,
data: action.data,
loading: false
});
default:
return state;
}
};
To address:
Objects are not valid as a React child(found objects with Keys
{source, author, title, description, url })
that's because you try to render Object:
{this.props.data}
but if you do:
{
this.props.data.map((el, i) =>
<p key={i}>Element nr {i}</p>
)
}
It should work.

State not updating with react redux thunk

I'm a bit new to using redux and react. I'm trying to make a simple API call with redux and having it render in react. I can see the API call working as it's in the payload in redux dev tools, but I can't seem to get it to update the state possibly in the `connect?.
actions/index
import FilmAPI from '../api/api';
export const FETCH_FILMS = 'FETCH_FILMS';
export const RECEIVE_FILMS = 'RECEIVE_FILMS';
export const receiveFilms = (films) => {
return {
type: RECEIVE_FILMS,
films
};
}
export const fetchFilmsRequest = () => {
return dispatch => {
return axios.get('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch(receiveFilms(response.data))
})
}
}
export default fetchFilmsRequest;
reducers/FilmReducer
import RECEIVE_FILMS from '../actions/index';
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...state, action.films];
default:
return state;
}
}
reducers/index
import { combineReducers } from 'redux';
import { films } from './FilmsReducer';
export default combineReducers({
films,
});
containers/FilmListContainer
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFilmsRequest } from '../actions';
import FilmList from '../components/FilmList'
class FilmListContainer extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchFilmsRequest();
}
render() {
return (
<div>
<FilmList films={this.props.films}/>
</div>
);
}
}
const mapStateToProps = state => ({
films: state.films
})
export default connect(mapStateToProps, {fetchFilmsRequest: fetchFilmsRequest})(FilmListContainer);
configureStore.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// options like actionSanitizer, stateSanitizer
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
return createStore(
rootReducer,
initialState,
enhancer
);
}
As mentioned, Redux DevTools show the films in the payload, but films still remain 0 in its state. Could anyone please point me in the right direction?
You can get updated state by subscribing store and use store.getState()
Steps:
Write subscribe function in constructor of component class.
Set state of class by store.getState().
import store from '../js/store/index';
class MyClass extends Component {
constructor(props, context) {
super(props, context);
this.state = {
classVariable: ''
}
store.subscribe(() => {
this.setState({
classVariable: store.getState().MyStoreState.storeVariable
});
});
}
}
You are close, your action needs to send the data to the store by dispatching an event which your reducer can then catch. This is done using the type attribute on the dispatch object.
https://redux.js.org/basics/actions
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response,
})
})
You then need to grab the data in your reducer and put it in the store
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return {
...state,
films: action.payload.films
};
default:
return state;
}
}
It looks like you just need to import your action type constant into your reducer using a named import instead of a default export.
i.e. import {RECEIVE_FILMS} from '../actions' rather than import RECEIVE_FILMS from '../actions'
Just dispatch result of resolved fetch promise like so:
if the payload is json, then:
export const fetchFilmsRequest = () => {
return dispatch => {
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => response.json())
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response
})
})
}
Your reducer would need modifying slightly to:
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...action.payload]; // assuming response is jus array of films
default:
return state;
}
}

Categories