React Native, Redux, Updates Overwrite initialState, Firebase - firebase

I am attempting to have default information in my User reducer and then add my Firebase login response to that default information. For example:
let userDefaultState = {startingNumber: 0}
const user = (state = userDefaultState, action) => {
switch (action.type) {
case 'RESET_USER':
return userDefaultState
case 'LOGIN':
return action.payload
case 'SET_MY_FEED':
return { ...state, myFeed: action.payload }
case 'UPDATE_FEED':
return { ...state, myFeed: [action.payload, ...state.myFeed]}
default:
return state
}
}
However, when I dispatch 'LOGIN', it fires Firebase.db.collection('user').doc(uid).get() and overwrites the current state with the response (ie payload) instead of adding it to the existing object. I believe it is because of the way the 'LOGIN' case is set up.
The following is incorrect but I think it illustrates what I'm trying to do.
return { ...state, [action.payload, ...state]}
Desired outcome would be something like this:
user = {
//Firebase response
"username": "name", "email": "email#email.com",
//userDefaultState
"startingNumber": 0
}
Thanks for any help!!

Related

Proper way to handle errors in redux using redux-promise

I'm trying "redux-promise".
When there's no error in the flow, my code works properly. But, let's say that the API is down or I have a typo in the URL. In those cases, I expect to handle the error in the proper way.
This is the API: https://jsonplaceholder.typicode.com/users
(in the snippet I'm adding random text at the end to produce the 404)
Action creator
export async function fetchUsers() {
const request = await axios
.get('https://jsonplaceholder.typicode.com/userssdfdsfdsf')
.catch(error => console.log('ERROR', error))
return {
type: FETCHING_USERS,
payload: request
};
}
Reducer
export default (state = [], action) => {
switch (action.type) {
case FETCHING_USERS:
return [...state, ...action.payload.data]
default:
return state
}
}
I can see the error logged in the console
ERROR Error: Request failed with status code 404
But, once the action is dispatched its payload is undefined
action {type: "FETCHING_USERS", payload: undefined}
I don't know where is the best place to handle this: action creator, reducer, etc. I shouldn't check if payload is something in the reducer and, if not, return state or do nothing. I want to understand which would be the best approach to handle this.
You may look at source of redux-promise, as it very simple.
Redux-promise expects either promise or action with payload set to some promise. I think you're going to use leter case.
Code may look like (just example, not tested):
export function fetchUsers() {
const request = axios.get('https://jsonplaceholder.typicode.com/userssdfdsfdsf');
return {
type: FETCHING_USERS,
payload: request
};
}
In this case redux-promise will await for resolution of promise returned by axios.get and dispatch your action but payload replaced with promise result. In case of error, redux-promise will catch it and dispatch action with error = true (you may want to handle action.error === true case in reducer)
In the reducer you should check for the existence of the error field in action:
export default function(state = null, action) {
if (action.error) {
//handle
}
switch(action.type) {
This code is for the action.
export const fetchUsers = () => async dispatch => {
try {
const res = await axios.get('https://jsonplaceholder.typicode.com/userssdfdsfdsf');
dispatch({
type: FETCHING_USERS,
payload: res.data
});
} catch (err) {
dispatch({
type: FETCHING_ERROR
});
}
};
In reducer do this.
Define the initial state and use this code.
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case FETCHING_USERS:
return {
...state,
loading: false,
user: payload,
};
case FETCH_ERROR:
return {
...state,
token: null,
loading: false,
};

Is it okay to use a 'hasFetched' with an 'isFetching' flag in redux state?

I'm new to redux thunk and have been using it to call an action creator that makes a request to an API to see if the user is logged in. If the user is not logged I want to redirect to the login page. I want a loader component to show while the user is still being fetched but also to wait until it has been fetched before either redirecting or showing the page component. This is what my user reducer looks like.
//User reducer
export default (state = { isFetching: false, hasFetched: false }, action) => {
switch (action.type) {
case GET_USER_REQUEST:
return { ...state, isFetching: true };
case GET_USER_SUCCESS:
return {
...state,
...action.payload,
isFetching: false,
hasFetched: true
};
case GET_USER_FAILURE:
return {
...state,
...action.payload,
isFetching: false,
hasFetched: true
};
default:
return state;
}
};
I can then use the simple function below to check if user state is ready:
const isStateReady = state => {
return !state.isFetching && state.hasFetched;
};
I was running into issues without the 'hasFetched' flag as the app would redirect to the login page before the fetch was complete.
Is this an okay way to handle my redux state in my whole application? Are there any other patterns?

Redux call function on change

I started using Redux with React. My Store contains several job Objects each with an id. I want to call a function, whenever I add/update a new Job. What's the recommended way to do so?
Here's my reducer
function reducer(state:= {}, action) {
const { id, fields } = action.payload
switch (action.type) {
case ADDED:
if (!state[id]) {
return {
...state,
[id]: fields,
},
};
} else {
const currentDate = moment(stat[id].lastModified)
const newDate = moment(fields.metaData.lastModified);
if (!currentDate.isSame(newDate)) {
return {
...state,
[id]: { ...state[id], ...fields },
},
};
}
// don't update otherwise
return state;
}
case OTHERS:
......
}
Whenever something is added, I add it to the state, if it already exists but the modificationDate is newer, I update it in the state. I want my function to the called in either ADDED cases
Reducers must do only state update (no more). It seems you need using Redux middleware for that

combine two redux reducers

I have a use case like this:
eventListReducer: will get a list of events based on date range
eventDetailReducer: will get the event details based on one event id
I know how to do the two above, my question:
When my page loads initially, I will get a list of events based on default date range and load the first event details, I can certainly create an
EventListAndDetailReducer to duplicate eventListReducer and eventDetailReducer. Is there any better way I can reuse the logic?
What I want to achieve is to have another action, that will first call getEvents and update the eventLists state, and then grab the first event and call setEvent and update the eventDetail state.
This is my eventDetailReducer:
const initialState = {
eventDetails: "",
}
const eventReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_EVENT":
state = {
...state,
eventDetails: action.payload
};
break;
}
return state;
}
export default eventReducer;
This is my eventsReducer:
const initialState = {
eventsList: [],
}
//getEventsReducer
const getEventsReducer = (state = initialState, action) => {
switch (action.type) {
case "GET_EVENTS":
state = {
...state,
eventList: ["Joe", "Tom", "Marry"] //assuming this from some other endpoint
};
break;
}
return state;
}
export default getEventsReducer;
What about using EventListAndDetailReducer?
const initialState = {
eventsList: [],
eventDetails: ""
}
export function eventListAndDetailReducer(state, action) {
switch(action.type) {
case GET_EVENTS:
return {...state, eventList: eventsReducer(state.eventsList, action)}
case "SET_EVENT":
return {...state, eventDetails: eventDetailsReducer(state.eventDetails, action)}
default:
return state
}
}
and then somewhen start using combineReducers?
Why not just have the eventDetails reducer also update on the GET_EVENTS action?
const eventReducer = (state = initialState, action) => {
switch (action.type) {
case "SET_EVENT":
state = {
...state,
eventDetails: action.payload
};
break;
case "GET_EVENTS":
state = {
...state,
eventDetails: action.payload[0] // assuming payload is an array
};
break;
}
return state;
}
Remember, all reducers receive all actions, so it does not need to be a 1-1 mapping.
What I understand from you question is that you want another action to do both actions sequentially and be dependent on each. I assume you have some middle ware such as redux-thunk that allows actions to be more than plaIn functions!
export function combinedAction() {
return (dispatch, getState) => {
// Write fetch() request to get events list from anywhere.
// Following should be within .then() if you're using fetch.
// Here events are just hardcoded in reducer!
dispatch(return { type: GET_EVENTS, payload: events }).then( () => {
let event = getState().eventsList[0]
dispatch(return { type: SET_EVENT, payload: event })
})
};
}
This will fire up GET_EVENTS action first and it'll set events array in state.eventsList. Then next action just uses this state information to dispatch next action SET_EVENT. Refer here to learn about chaining actions. How to chain async actions?

Fetch data from API wtih Redux

Recently I've been looking into react and redux. I read up the official documentation and tried some ToDo List tutorials. Part 1 is just about react and this is part 2 about redux:
http://www.theodo.fr/blog/2016/03/getting-started-with-react-redux-and-immutable-a-test-driven-tutorial-part-2/
So basically he just sets up a store and initially adds an array of a few todos. Now I don't want my data to be local and I want to fetch it from an API. I'm having a hard time understanding how this actually works. So the code I would use in my action_creators.js is:
export function fetchData() {
return dispatch => {
fetch('http://127.0.0.1:8000/example')
.then(res => res.json())
.then(res => dispatch({
type: FETCH_DATA,
data: res
}))
}
}
Now in the example code for example adding a 'todo':
export function addItem(text) {
return {
type: 'ADD_ITEM',
text
}
}
You aren't dispatching anything, the tutorial does this in the reducer? But when you return dispatch your fetch, does this automatically get dispatched to your store?
If so I have no clue what I should write in my reducer ..
This is the code I have for adding a 'todo':
import {Map} from 'immutable';
function setState(state, newState) {
return state.merge(newState);
}
function fetchData(state) {
return state;
}
function addItem(state, text) {
const itemId = state.get('hostnames').reduce((maxId, item) => Math.max(maxId,item.get('id')), 0) + 1;
const newItem = Map({id: itemId, text: text, status: 'active'});
return state.update('hostnames', (hostnames) => hostnames.push(newItem));
}
export default function(state = Map(), action) {
switch (action.type) {
case 'SET_STATE':
return setState(state, action.state);
case 'ADD_ITEM':
return addItem(state, action.text);
case 'FETCH_DATA':
return fetchData(state);
}
return state;
}
So basically my question is, how do I fetch the data ( if the fetch is wrong now ) and how do I add the fetched data from my api to the store in my reducer.
I just find react and redux pretty complicated so sorry if I'm asking a really noob question or just making big mistakes in the way I want to do something.
Thanks in advance for any help.
imagine your json
{
"data": {
"apple": 1,
"banana": 3,
},
"status": 200,
}
your actions
export function fetchData() {
return dispatch => {
fetch('http://127.0.0.1:8000/example')
.then(res => res.json())
.then((responseData) => {
if(responseData.status === 200){
dispatch(setData(responseData));
}
})
}
}
export function setData(responseData) {
return {type: SET_DATA, data: responseData.data }
}
your reducer
const initialState = { data: null };
export default function(state = initialState, action) {
switch (action.type) {
case 'SET_DATA':
return Object.assign({}, state, {
data: action.data,
})
default:
return state;
}
}
then your state will become
{ data: {
apple: 1,
banana: 3,
}
}
Actually, all your reducers should be pretty dumb and pure (without any side effects). So their only concern is to modify the state and nothing else. Fetching data from the server or any kind of orchestration should be implemented in redux middleware. Look at redux-thunk or redux-saga if you need something more complicated. Hope that helps.

Resources