I have two methods in two different actions,
import { fetchCategories } from "../../../actions/elementsActions"
import { fetchPlaces } from "../../../actions/placesActions"
And the componentWillMount method is:
componentWillMount() {
this.props.dispatch(fetchCategories())
this.props.dispatch(fetchPlaces())
}
I want to make sure fetchCategories is fetched before fetchPlaces. Is this the right way of doing it?
UPDATE
Actions:
import axios from "axios";
export function fetchPlaces() {
return function(dispatch) {
axios.get("/getPlaces")
.then((response) => {
console.log(response);
dispatch({type: "FETCH_PLACES_FULFILLED", payload: response.data})
})
.catch((err) => {
dispatch({type: "FETCH_PLACES_REJECTED", payload: err})
})
}
}
Reducer :
export default function reducer(
state={
places: [],
fetching: false,
fetched: false,
error: null,
}, action) {
switch (action.type) {
case "FETCH_PLACES": {
return {...state, fetching: true}
}
case "FETCH_PLACES_REJECTED": {
return {...state, fetching: false, error: action.payload}
}
case "FETCH_PLACES_FULFILLED": {
return {
...state,
fetching: false,
fetched: true,
places: action.payload,
}
}
}
return state
}
Store :
import { applyMiddleware, createStore } from "redux"
import logger from "redux-logger"
import thunk from "redux-thunk"
import promise from "redux-promise-middleware"
import reducer from "./reducers"
const middleware = applyMiddleware(promise(), thunk, logger())
export default createStore(reducer, middleware)
Dispatch is synchronous, but this will only guarantee that fetchCategories is fired (not fetched) before fetchPlaces. You need to remove this.props.dispatch(fetchPlaces()) from componentWillMount() and add it inside the then((response) of fetchPlaces() to guarantee that it fires after a successful fetch of fetchPlaces().
Related
I dispatched an action in a react-redux application, the states of the reducer with that action was updated with the payload from the action as required. In addition to this, the states of the another reducer that does not not have such an action was updated with that same payload even when the corresponding action was not dispatched. what exactly is happening?
//my action for reducer 1
import { CREATE, UPDATE, DELETE, FETCH_ALL, ITEMS_LOADING } from '../constants/actionTypes';
import * as api from '../api/index';
export const getPosts = () => async (dispatch) => {
dispatch(setItemsLoading());
const data = await api.fetchPosts
.then((res) => {
dispatch({ type: FETCH_ALL, payload: res.data });
}).catch(() => {
console.log("there is error");
})
}
//my action for reducer 2
import { GET_POST } from '../constants/actionTypes';
import * as api from '../api/index';
export const getSinglePosts = (id) => (dispatch) => {
return{
type: GET_POST, payload: id
}
}
//reducer 1
import { CREATE, UPDATE, DELETE, FETCH_ALL, ITEMS_LOADING } from '../constants/actionTypes';
const initialState = {
posts: [],
allLoaded:false,
loading: false
}
export default (state = initialState, action) => {
switch (action.type) {
case DELETE:
return {
...state,
posts: state.posts.filter((post) => post._id !== action.payload)
};
case UPDATE:
return {
...state,
posts: state.posts.map((post) => post._id === action.payload._id ? action.payload : post)
};
case FETCH_ALL:
return {
...state,
posts: action.payload,
allLoaded:true,
loading: false
};
case CREATE:
return {
...state,
posts: [action.payload, ...state.posts]
};
case ITEMS_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
//reducer2
import {
GET_POST
} from '../constants/actionTypes';
const initialState = {
info:null,
postLoaded:false
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_POST:
return {
...state,
postLoaded: true,
info:action.payload
};
default:
return state;
}
}
//homepage where action was dispatched
import React, { useState, useEffect } from 'react';
import PageNavbar from './PageNavbar';
import { getPosts } from '../../myActions/posts';
import { useSelector, useDispatch } from 'react-redux';
//import { getSinglePosts } from '../../myActions/single';
import { useHistory } from 'react-router-dom';
import Button from '#material-ui/core/Button';
function MyBlog() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
const posts = useSelector((state) => state.posts.posts);
const history = useHistory();
return (
<>
<PageNavbar />
<div align="center">
{posts.map((post) => (
<>
<Button href="/user/singlePost" color="primary">
{post.title}
</Button>
<br />
</>))}
</div>
</>
)
}
export default MyBlog
//root reducer
import { combineReducers } from 'redux';
import posts from './myposts';
import error from './error';
import auth from './auth';
import info from './singlePost';
export default combineReducers({
info,
posts,
auth,
error
})
//thanks
I'm working with React-Native and Redux.
I need to access my User state on react redux after an action.
I try this with a simple console.log after the action on my Component (console.log(this.props.User)) but it is always undefined.
This is my Combine Reducer:
import { combineReducers } from 'redux'; import User from './userReducer';
export default combineReducers({
User });
This is my User Reducer:
import {
REGISTER_USER,
SIGNIN_USER } from '../types';
export default function(state=INITIAL_STATE, action){
switch(action.type){
case SIGNIN_USER:
return {
...state,
userData:{
uid: action.payload.localId || false,
token: action.payload.idToken || false,
refreshToken: action.payload.refreshToken || false,
}
};
break;
default:
return state
} }
This is my action:
export function signIn(data){
const request = axios({
method:'POST',
url:SIGNIN,
data:{
email: data.email,
password: data.password,
returnSecureToken:true
},
headers:{
"Content-Type":"application/json"
}
}).then(response => {
return response.data
}).catch( e=> {
console.log(e)
});
return {
type: SIGNIN_USER,
payload: request
}
}
Component where I try to get the state after action:
import { connect } from 'react-redux';
import { signIn } from '../Store/Actions/userActions';
import { bindActionCreators } from 'redux';
class LoginComponent extends React.Component {
state = {
email:'',
password:''
};
signInUser () {
try {
this.props.signIn(this.state).then( () => {
**console.log(this.props.User)**
})
} catch (error) {
console.log(error)
}
}
}
const mapStateToProps = (state) => {
return{
User: state.User
}
}
const mapDispatchToProps =( dispatch ) => {
return bindActionCreators({signIn}, dispatch)
}
export default connect(mapDispatchToProps,mapDispatchToProps)(LoginComponent);
The response on log just is: undefined
What is my mistake?? Thanks!
The order of the arguments that you are passing into connect method is wrong it expects mapStateToProps first and mapDispatchToProps as second
parameter. So your code has to be export default connect(mapStateToProps, mapDispatchToProps)(LoginComponent);
Here is the signature
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
I want to use redux to call API in react native. Redux is ok when I want to call only single API in a component but when I want to dispatch more than one API. it just mix up and i can not handle in componentWillRecieveProps.
this is my component which I want to call API in it:
import React, { Component } from 'react';
import { Alert,View,Text,Button } from 'react-native';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import { generateOTP,getAnother } from '../Actions'
class Splash extends Component {
constructor(props) {
super(props);
this.onPress=this.onPress.bind(this);
this.onClick=this.onClick.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps !== this.props) {
if( nextProps.api.status === 'api_success' && nextProps.api.error===null) {
Alert.alert("yes successfully take data");
}
if(nextProps.myApp.status === 'success' && nextProps.myApp.error === null){
Alert.alert("you call second api");
}
}
}
onClick(){
this.props.dispatch(getAnother());
}
onPress(){
this.props.dispatch(generateOTP());
}
render() {
return (
<View>
<Button title="Click me first" onPress={this.onPress}/>
<Text>Extraa space</Text>
<Text>Extraa space</Text>
<Button title="Click me" onPress={this.onClick}/>
</View>
);
}
}
export default connect(state => state)(Splash)
To tell you exactly when i click on Click me button both condition inside this if (nextProps !== this.props) block will be true and trigger Alert to me.
api-reducer.js:
const initialState = {
data: {},
error: null,
status: null
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case "GENERATE_OTP_PENDING":
// Action is pending (request is in progress)
return {...state, status: 'fetching'}
case "GENERATE_OTP_FULFILLED":
// Action is fulfilled (request is successful/promise resolved)
return {
...state,
error: null,
data: action.payload.data,
status: 'api_success'
}
case "GENERATE_OTP_REJECTED":
// Action is rejected (request failed/promise rejected)
return {
...state,
error: action.payload,
status: 'api_error'
}
default:
return state;
}
};
tagreducer.js:
const initialState = {
data: {},
error: null,
status: null
};
export default function reducer(state = initialState, action) {
switch (action.type) {
case "GET_INFORMATION_PENDING":
// Action is pending (request is in progress)
return {...state, status: 'fetching'}
case "GET_INFORMATION_FULFILLED":
// Action is fulfilled (request is successful/promise resolved)
return {
...state,
error: null,
data: action.payload.data,
status: 'success'
}
case "GET_INFORMATION_REJECTED":
// Action is rejected (request failed/promise rejected)
return {
...state,
error: action.payload,
status: 'error'
}
default:
return state;
}
};
this is the way I index or combine both recuder:
import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import api from './api-reducer';
import myApp from './tagreducer';
export default combineReducers({
api,
myApp,
routing: routerReducer,
});
The Actions.js:
import axios from 'axios';
import * as config from '../Config';
export const generateOTP = () => ({
type: 'GENERATE_OTP',
payload: axios({
method: 'get',
url: 'https://jsonplaceholder.typicode.com/posts',
headers: {"Accept": "application/json"}
})
});
export const getAnother = () => ({
type: 'GET_INFORMATION',
payload: axios({
method: 'get',
url: 'https://newsapi.org/v2/sources?apiKey=94da7332cf0a4b1ea685afc9a6829078',
headers: {"Accept": "application/json"}
})
});
and finally my Store.js:
import { applyMiddleware, createStore } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise-middleware';
import logger from 'redux-logger'
import reducers from './Reducers';
export default function configureStore(history) {
const middleware = applyMiddleware(
promiseMiddleware(),
thunk,
logger,
routerMiddleware(history));
return createStore(reducers, {}, middleware);
}
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;
}
}
After one of the Redux tutorials decided to implement that facny action->reducer->store->view chain for simple app with only login part.
Seems like all setted up but when I run my app - in mapStateToProps(currentState) no any sign of the any custom state fields which I expected to see! (default state from reducer). But the action function is fine, as you can see on the screenshot
I can't see whats wrong here so, decided to ask it.
So here is the code
So, first of all - store.js
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
if (module.hot) {
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers');
store.replaceReducer(nextRootReducer);
});
}
return store;
}
then login reducer
const initialState = {
user: {
name: '',
password: ''
},
fetching: false
}
export default function login(state = initialState, action) {
switch (action.type) {
case LOGIN_REQUEST: {
return { ...state, fetching: true }
}
case LOGIN_SUCCESS: {
return { ...state, user: action.data, fetching: false }
}
case LOGIN_FAIL: {
return { ...state, user: -1, fetching: false }
}
default: {
return state;
}
}
}
and the root (reducers/index.js):
import login from './login/login';
import { combineReducers } from 'redux'
export default combineReducers({
login
});
the action
import {
LOGIN_REQUEST,
LOGIN_SUCCESS,
LOGIN_FAIL
} from '../../constants/login.js';
export function onLoginAttempt(userData) {
return (dispatch) => {
dispatch({
type: LOGIN_REQUEST,
user: userData
})
tryLogin(userData);
}
};
function tryLogin(userData) {
let url = 'SignIn/Login ';
return (dispatch) => {
axios.post(url, userData)
.then((response) => dispatch({
type: LOGIN_SUCCESS,
data: response.data
})).error((response) => dispatch({
type: LOGIN_FAIL,
error: response.error
}))
}
};
So here is entrance point:
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './containers/app.js';
import configureStore from './store/configureStore';
let store = createStore(configureStore);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("content")
);
and here is the app.js (Login is just sompe custom div with two fields nothing special)
import React, { Component } from 'react';
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import Login from '../components/login/Login';
import * as pageActions from '../actions/login/login'
class App extends Component {
render() {
const { user, fetching } = this.props;
const { onLoginAttempt } = this.props.pageActions;
return <div>
<Login name={user.name} password={user.password} fetching={fetching} onLoginAttempt={onLoginAttempt} />
</div>
}
}
function mapStateToProps(currentState) {
return {
user: currentState.user,
fetching: currentState.fetching
}
}
function mapDispatchToProps(dispatch) {
return {
pageActions: bindActionCreators(pageActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
I see that you have state which looks like:
reduxState = {
login: {
user: {
name: '',
password: ''
},
fetching: false
}
}
but then you try to access properties that don't exist.
function mapStateToProps(currentState) {
return {
user: currentState.user,
fetching: currentState.fetching
}
}
I think you need to:
function mapStateToProps(currentState) {
return {
user: currentState.login.user,
fetching: currentState.login.fetching
}
}
You have this line let store = createStore(configureStore); on your entry point. However, inside configureStore, you have a call to createStore()
Basically you're calling something like createStore(createStore(reducers)). That's probably the cause of the problem.
You should probably call it like
let store = configureStore( /* pass the initial state */ )