Redux Reducer - State - redux

So I'm just getting started with redux.I am trying to fetch some gifs from an API.
My goal is to return an array of img urls instead of the whole response data.However if I try to iterate and return each image my state remains null.If I use return action.payload.data.data; which returns the whole response then this works but I only want to get the img urls.
Here is the corresponding reducer.
import _ from 'lodash';
export default function(state = null,action) {
switch(action.type) {
case 'FETCH_GIF':
_.forOwn(action.payload.data.data, function(value, key) {
var img = value.images.downsized.url;
return img;
});
// return action.payload.data.data;
default:
return state;
}
}

state = null looks a bit wrong, it should normally default to some kind of initialState object. Then in your case block it's not really clear what the _.forOwn is supposed to return. Are you trying to mutate action.data.data? If so, maybe check if that is actually happening. Also your state returned by the reducer should be an object, I don't think redux works when you return an array.

Related

I am trouble to understand what is returning from the function when using the ES6 script. I am new to ES6, react and redux world

I am new to react and redux trying to understand below code:
const initialState = { list: [] };
export default function (state = initialState , action) {
switch (action.type) {
case GET_TICKET:
return {
...state,
list: [...list, action.payload.data]
};
default:
return state;
}
}
What is the return statement doing here? Is it returning the two arrays i.e state and list?
If the reducer updates state, it should not modify the existing state object in-place. Instead, it should generate a new object containing the necessary changes.
The case for handling the GET_TICKET action will add a new list item to the state.list and return a shallow copy(by es6 spread operator) instead of mutating the state.list in place.
From the doc Immutable Update Patterns:
The key to updating nested data is that every level of nesting must be copied and updated appropriately.
Also, see Inserting and Removing Items in Arrays

How to correctly return array in redux state, if the array did not have to be updated in the reducer?

I am using the aurelia-store state management library for managing state. This question is not specific to Aurelia store, but actually to redux best practices in general since Aurelia store is very much the same thing.
I have an action that fetches unit updates from an API like so:
export const fetchNewUnits = async (state: State): Promise<State> => {
const fetchedUnits = await apiClient.getUnitsMarkers();
// no new updates so don't trigger change in units
// IS THIS ACCEPTABLE?
if (fetchedUnits.length === 0) {
return {
...state,
highwaterMark: new Date()
};
}
const units: UnitMarker[] = state.units.slice();
_.forEach(fetchedUnits, (newUnit) => {
// look for matching unit in store
const idx = _.findIndex(units, {
imei: newUnit.imei
});
// unit was found in store, do update
if (idx !== -1) {
// replace the unit in the store
const replacement = new UnitMarker({...newUnit});
units.splice(idx, 1, replacement);
}
});
// OR SHOULD I ALWAYS DEEP COPY THE ARRAY REFERENCE AND IT'S OBJECTS
return {
...state,
highwaterMark: new Date(),
units: [...units]
};
};
If I do not have any unit changes (i.e. my store is up to date) can I simply return the state with the spread operator as shown in the first return statement? Is this fine since I did not modify the objects?
Or do I always have to do deep replacements such as:
return {
...state,
highwaterMark: new Date(),
units: [...state.units]
};
even if the objects in the array did not change?
The reason why you’re supposed to create a new object is because React components check for prop changes in order to know when to re-render.
If you simply modify an object and pass it in as a prop again, React won’t know that something changed and will fail to rerender.
So in your case, the question is: do you want to rerender, or not? If you don’t, returning the same object is fine and a simple ‘return state’ will let React know that no rerenders are necessary.
See: Why is the requirement to always return new object with new internal references

React Redux Search Reducer

Currently I have the below reducer switch statement. All it does is toggles the state of Sidebar, so first it shows then hides then shows. It's easy.
switch(action.type) {
case 'SIDEBAR_DISPLAY_TOGGLE':
return {
...state,
Sidebar : {
...state.Sidebar,
Display : !state.Sidebar.Display
}
}
default:
return state;
}
Now I have a input field like here
that people can type to search account. I am trying to set up Redux so when user types, it gets saved to the Redux global state and I can pull it from another component. I have this reducer code set up for it but I don't know how can I pull what user types into this reducer from that component?
function reducer(state = initialState, action) {
switch(action.type) {
case 'ACCOUNT_SEARCH':
return {
...state,
AccountNumberSearch : {
...state.AccountNumberSearch,
AccountNumber : ''
}
}
default:
return state;
}
}
}
An action is just an object with a string value named type. Any other properties on this object will also be passed, so you use this to pass the typed text.
If you're using a function to create your actions, something along the lines of:
export function accountNumberSearch(accountNumber) {
return { type: 'ACCOUNT_SEARCH', accountNumber };
}
Then in your reducer, you'll be able to assign the value in the state to action.accountNumber.
AccountNumberSearch : {
...state.AccountNumberSearch,
AccountNumber : action.accountNumber,
}
Then you can map your state to props as you normally would (as you did for the sidebar toggle).
Also, as an aside, you should look into modularising your reducers with combineReducers - Docs
This would be much easier than the way you're doing it.
EDIT: Handling the changes
First of all, you'd want to wire up your input field for the search box to an onChange listener. If you do this like onChange={this.onSearchChange} you can get the value from event in the function:
onSearchChange = event => {
this.props.AccountNumberSearch(event.target.value);
}
Then in mapDispatchToProps you'd send your action + the passed value to dispatch:
const mapDispatchToProps = dispatch => {
return {
AccountNumberSearch: AccountNumber => dispatch(importedActions.AccountNumberSearch(AccountNumber)),
}
}
Then, in the component you want to RECEIVE this value, you'd map the redux state to props, like:
const mapStateToProps = state => {
return {
AccountNumber: state.AccountNumberSearch.AccountNumber,
}
}
Then you can access that value in your render function by calling this.props.AccountNumber.
If you need to do something when this value changes, you can always listen on componentDidUpdate, and compare the value with the old one - if it changed, call whatever function that you need to do.

Can passing an action object an empty object upon initialization affect application state?

I have this function here that has an action given an empty object:
export default reducer(state = initialState, action = {}) {
switch(action.type) {
return {
...state, blahblah
}
}
}
Is it possible this could create some type of bug or better framed, why could this be a bad practice? With my understanding of how reducers work, I don't see the point in giving initializing the action with an empty object.
I dont see the point either. Your action creators will(be required to) always return an object with a type/payload so seems unnecessary to set a default

React-redux project - chained dependent async calls not working with redux-promise middleware?

I'm new to using redux, and I'm trying to set up redux-promise as middleware. I have this case I can't seem to get to work (things work for me when I'm just trying to do one async call without chaining)
Say I have two API calls:
1) getItem(someId) -> {attr1: something, attr2: something, tagIds: [...]}
2) getTags() -> [{someTagObject1}, {someTagObject2}]
I need to call the first one, and get an item, then get all the tags, and then return an object that contains both the item and the tags relating to that item.
Right now, my action creator is like this:
export function fetchTagsForItem(id = null, params = new Map()) {
return {
type: FETCH_ITEM_INFO,
payload: getItem(...) // some axios call
.then(item => getTags() // gets all tags
.then(tags => toItemDetails(tags.data, item.data)))
}
}
I have a console.log in toItemDetails, and I can see that when the calls are completed, we eventually get into toItemDetails and result in the right information. However, it looks like we're getting to the reducer before the calls are completed, and I'm just getting an undefined payload from the reducer (and it doesn't try again). The reducer is just trying to return action.payload for this case.
I know the chained calls aren't great, but I'd at least like to see it working. Is this something that can be done with just redux-promise? If not, any examples of how to get this functioning would be greatly appreciated!
I filled in your missing code with placeholder functions and it worked for me - my payload ended up containing a promise which resolved to the return value of toItemDetails. So maybe it's something in the code you haven't included here.
function getItem(id) {
return Promise.resolve({
attr1: 'hello',
data: 'data inside item',
tagIds: [1, 3, 5]
});
}
function getTags(tagIds) {
return Promise.resolve({ data: 'abc' });
}
function toItemDetails(tagData, itemData) {
return { itemDetails: { tagData, itemData } };
}
function fetchTagsForItem(id = null) {
let itemFromAxios;
return {
type: 'FETCH_ITEM_INFO',
payload: getItem(id)
.then(item => {
itemFromAxios = item;
return getTags(item.tagIds);
})
.then(tags => toItemDetails(tags.data, itemFromAxios.data))
};
}
const action = fetchTagsForItem(1);
action.payload.then(result => {
console.log(`result: ${JSON.stringify(result)}`);
});
Output:
result: {"itemDetails":{"tagData":"abc","itemData":"data inside item"}}
In order to access item in the second step, you'll need to store it in a variable that is declared in the function scope of fetchTagsForItem, because the two .thens are essentially siblings: both can access the enclosing scope, but the second call to .then won't have access to vars declared in the first one.
Separation of concerns
The code that creates the action you send to Redux is also making multiple Axios calls and massaging the returned data. This makes it more complicated to read and understand, and will make it harder to do things like handle errors in your Axios calls. I suggest splitting things up. One option:
Put any code that calls Axios in its own function
Set payload to the return value of that function.
Move that function, and all other funcs that call Axios, into a separate file (or set of files). That file becomes your API client.
This would look something like:
// apiclient.js
const BASE_URL = 'https://yourapiserver.com/';
const makeUrl = (relativeUrl) => BASE_URL + relativeUrl;
function getItemById(id) {
return axios.get(makeUrl(GET_ITEM_URL) + id);
}
function fetchTagsForItemWithId(id) {
...
}
// Other client calls and helper funcs here
export default {
fetchTagsForItemWithId
};
Your actions file:
// items-actions.js
import ApiClient from './api-client';
function fetchItemTags(id) {
const itemInfoPromise = ApiClient.fetchTagsForItemWithId(id);
return {
type: 'FETCH_ITEM_INFO',
payload: itemInfoPromise
};
}

Resources