I'm using MyJSON to create a store for my data and im having trouble looping through it
i've tried .map(), .forEach but i cannot for the life of me, map over the array of objects.
TypeError: Cannot read property 'map' of undefined
the JSON store looks like this
const Leaderboard = (props) => {
useEffect(() => {
props.fetchScores();
},[]);
const renderList = () => {
props.scores.map(item => {
return <LeaderboardItem name={item.name} data={item.date} />
})
}
return (
<div className="leaderboard">
<h1 className='leaderboard__header'> Leaderboard</h1>
{renderList()}
</div>
);
};
const mapStateToProps = (state) => {
return {
scores: state.scores
};
};
export default connect(mapStateToProps, { fetchScores })(Leaderboard);
I'm able to fetch the data, add it to my reducers. When i console.log my props i get this
(5) [{…}, {…}, {…}, {…}, {…}]
0: {name: "ryan", score: 3, date: 1564079441826, id: 1}
1: {name: "ryan", score: 0, date: 1564080251976, id: 2}
2: {name: "ryan", score: 4, date: 1564080621616, id: 3}
3: {name: "ryan", score: 1, date: 1564088666800, id: 4}
4: {name: "ryan", score: 8, date: 1564088919233, id: 5}
length: 5
__proto__: Array(0)
shouldn't I be able to map over the array and return each object?
10 | },[]);
11 |
12 | const renderList = () => {
> 13 | props.scores.map(item => console.log(item))
| ^ 14 | }
15 |
16 | return (
export default (state = {}, action) => {
switch(action.type) {
case FETCH_SCORES:
return { ...state, ...action.payload }
default:
return state;
};
};
images :
Redux-dev-tools-image1
Redux-dev-tools-image1
console.log
to render a list of items you need to actually return JSX in your map
const renderList = () => {
props.scores.map(item => <div key={item.id}>{item.name}</div>)
}
You should read up on best practices for rendering lists of elements in react.
Edit
Since scores is undefined, you need to make sure that you are referencing things correctly.
Is scores the key defined in combineReducers? aka something like combineReducers({scores: scoresReducer})
Update your reducer to store what you want to store all the time. dont change the datatypes
const defaultState = {
scores: []
}
export default (state = defaultState, action) => {
switch(action.type) {
case FETCH_SCORES:
return { ...state, scores: ...action.payload }
default:
return state;
};
}
this assumes action.payload is an array of scores, adjust accordingly if its not
map creates another array as a transformation of the original array. You are simply console.log'ing over each item which would be a use for forEach and not map.
The return value of a console.log is undefined and probably causing problems while rendering an array of [undefined, undefined, ...]
Can you consider this:
props.scores && props.scores.map(item => {
return <LeaderboardItem name={item.name} data={item.date} />
})
Related
the title may be misleading but here is what happened:
reducer.js:
// initial state
const initialState = {
notes: [
{
content: "reducer defines how redux store works",
important: true,
id: 1,
},
{
content: "state of store can contain any data",
important: false,
id: 2,
},
],
filter: "IMPORTANT",
};
// reducer
const noteReducer = (state = initialState, action) => {
switch (action.type) {
case "NEW_NOTE":
console.log("state", state);
return state.notes.concat(action.data);
// ...
}
const generateId = () => Math.floor(Math.random() * 1000000);
// action
export const createNote = (content) => {
return {
type: "NEW_NOTE",
data: {
content,
important: false,
id: generateId(),
},
};
};
in index.js:
const reducer = combineReducers({
notes: noteReducer,
filter: filterReducer,
});
const store = createStore(reducer, composeWithDevTools());
//dispatch a note from index.js
//it works here
store.dispatch(
createNote("combineReducers forms one reducer from many simple reducers")
);
returns in the console.log("state", state); in reducer.js:
state
{notes: Array(2), filter: 'IMPORTANT'}
filter: "IMPORTANT" // 'filter' is here
notes: (2) [{…}, {…}]
[[Prototype]]: Object //prototype is object
Here createNote is successful.
However, when creating a new note through:
const NewNote = (props) => {
const dispatch = useDispatch();
const addNote = (event) => {
event.preventDefault();
const content = event.target.note.value;
event.target.note.value = "";
// createNote does not work here
dispatch(createNote(content));
};
return (
<form onSubmit={addNote}>
<input name="note" />
<button type="submit">add</button>
</form>
);
};
Here the console.log("state", state); returns:
state
(3) [{…}, {…}, {…}]
0: {content: 'reducer defines how redux store works', important: true, id: 1}
1: {content: 'state of store can contain any data', important: false, id: 2}
2: {content: 'combineReducers forms one reducer from many simple reducers', important: false, id: 824517}
length: 3
// 'filter' is missing
[[Prototype]]: Array(0) // state prototype changed to array
In which the filter is gone from the state, so the creation is not successful.
In short, store.dispatch( createNote("...") ); works but not dispatch(createNote(content));.
The reason seems to be that noteReducer received different states. But in both cases filter is not specified.
I wonder why this happens and how to solve it?
as we know when you are using a reducer the reducer takes 2 parameters one for initial stat and the other for action.
any action will be run in the reducer you need to save the old state
by used spread operator {...state}
case "NEW_NOTE":
console.log("state", state);
{...state, notes: state.notes.concat(action.data)}
found the issue.
noteReducer should be:
const noteReducer = (state = initialState, action) => {
switch (action.type) {
case "NEW_NOTE":
return { ...state, notes: state.notes.concat(action.data) };
//...
}
just found out that above is a wrong fix. the right one is actually is:
const noteReducer = (state = initialState.notes, action) => {
otherwise noteReducer is changing the filter as well. but it should be changing the 'note' part only.
I have a problem. I would like to get some data before loading the DOM but I have been running into this error for hours. I have a 200 response on my query but the error persists. After a reload of the page the display is ok.
// redux
const dispatch = useDispatch();
const customers = useSelector((state) => state.customerReducer);
useEffect( () => {
dispatch(findAllCustomers());
}, [])
{ !isEmpty(Object.values(customers)) && Object.values(customers)[0].map((customer, index) => ...
Uncaught TypeError: Object.values(...)[0].map is not a function ...
Thanks for your help.
[Array(54)]
0: (54) [{…}, {…}, {…}, {…},
0: Array(54)
0: {id: 2,, …}
1: {id: 3, …}
2: {id: 4 , …}
//Actions.js
export const findAllCustomers = () => {
return (dispatch) => {
axios.get('/api/customers')
.then((response) => {
dispatch({
type: FIND_ALL_CUSTOMERS, payload:response.data
})
})
.catch((error) => console.log(error.response))
}
}
//CustomersReducer.js
const INITIAL_STATE = [];
function customerReducer(state = INITIAL_STATE, action)
{
switch (action.type){
case 'FIND_NB_CUSTOMERS' : {
return {
...state,
nbCustomers : action.payload
}
}
case 'FIND_ALL_CUSTOMERS' : {
return {
...state,
customers: action.payload
}
}
default:
return state
}
}
export default customerReducer;
//isEmpty()
export const isEmpty = (value) => {
console.log(value)
return (
value === undefined ||
value === null ||
(typeof value === "object" && Object.keys(value).length ===
0) ||
(typeof value === "string" && value.trim().length === 0)
);
}
Object.values(customers) return an Array and you are trying to access the first index of that array which is probably not an Array anymore.
Change to:
Object.values(customers).map()
The problem is that you are trying to use map() on something that isn't an array. Object.values(customers)[0] is an object. However, since customers is an array, there is no reason to use Object.values() at all. Instead, just map over the array directly with customers.map().
So all together it should be
{ customers && customers.map(...) }
Hello I am doing an app in React with typescript and I am having a issue when comparing two states(which basically are 2 objects arrays and filtering so I only get back the ones that are ont included in allBooks array, this is the interfaces:
export interface IBook {
book_id: number;
book_name: string;
release_year: number;
}
then the states of my class:
class Home extends Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
users: [],
userBooks: [],
allBooks: []
};
}
and then the compare function:
filterBooks = () => {
const { allBooks, userBooks } = this.state;
let notUserBooks = allBooks.filter(
({ book_id }: { book_id: number }) => !userBooks.includes({book_id}));
console.log(notUserBooks, allBooks);
};`
I do not know what I am doing wrong but the console.log I get the same results in notUserBooks than in allBooks, I mean the allBooks has 3 items in it, and in the console log after the filter I get the whole 3 items....this is the console output:
(3) [{…}, {…}, {…}]
0: {book_name: "the lord of the rings", release_year: 1950}
1: {book_name: "the butterfly", release_year: 1980}
2: {book_name: "the hobbit", release_year: 1960}
length: 3
__proto__: Array(0)
(3) [{…}, {…}, {…}]
0: {book_name: "the lord of the rings", release_year: 1950}
1: {book_name: "the butterfly", release_year: 1980}
2: {book_name: "the hobbit", release_year: 1960}
length: 3
__proto__: Array(0)```
Try changing this:
filterBooks = () => {
const { allBooks, userBooks } = this.state;
let notUserBooks = allBooks.filter(
({ book_id }: { book_id: number }) => !userBooks.includes({book_id}));
console.log(notUserBooks);
};
to this:
filterBooks = () => {
const { allBooks, userBooks } = this.state;
let notUserBooks = allBooks.filter(
({ book_id }: { book_id: number }) => !userBooks.map(book => book.book_id).includes(book_id));
console.log(notUserBooks);
};
In general, you cannot compare Objects in javascript. A simple test:
const a = { test: 'test' };
const b = { test: 'test' };
alert(a === b);
You can use Array.some() to check if userBooks contains the book item:
let notUserBooks = allBooks.filter((book) => !userBooks.some((userBook) => userBook.book_id === book.book_id));
console.log(notUserBooks);
Objects are reference types so you can’t just use === or == to compare objects.
One solution for your filter method is (It is split into 2 methods):
const bookIds = (books: IBook[]): number[] => !!books && books.map(({bookId})=> bookId);
const filterBooks = (userBooks: IBook[], allBooks: IBook[]): IBook[] =>
!!allBooks && allBooks.filter(({bookId}) => bookIds(userBooks).indexOf(bookId) === -1 );
In this project you can see it better (with Typescript & React)
I have an app which get all categories and products from the server with Redux ACTIONS. I need to filter products with a category Id. after load data action is complete, i call another action to filter products but i'm a little bit confused.
There is codes of few parts of the app:
ProductsActions:
export const GET_INITIAL_PRODUCTS_DATA = "GET_INITIAL_PRODUCTS_DATA";
export const GET_INITIAL_PRODUCTS_DATA_RESULT = "GET_INITIAL_PRODUCTS_DATA_RESULT";
export const GET_INITIAL_PRODUCTS_DATA_ERROR = "GET_INITIAL_PRODUCTS_DATA_ERROR";
export const FILTER_PRODUCTS_BY_CATEGORY_ID = "FILTER_PRODUCTS_BY_CATEGORY_ID";
export const getInitialProductsData = () => ({
type: GET_INITIAL_PRODUCTS_DATA
});
export const filterProductsByCategoryId = categoryId => ({
type: FILTER_PRODUCTS_BY_CATEGORY_ID,
categoryId
});
ProductsReducers:
import {
GET_INITIAL_PRODUCTS_DATA,
GET_INITIAL_PRODUCTS_DATA_RESULT,
GET_INITIAL_PRODUCTS_DATA_ERROR,
FILTER_PRODUCTS_BY_CATEGORY_ID
} from "../actions/products";
const initialState = {
isFetching: false,
data: {},
error: null
};
const filterProductsByCategoryId = (state, action) => {
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case GET_INITIAL_PRODUCTS_DATA:
return {
...state,
isFetching: true
};
case GET_INITIAL_PRODUCTS_DATA_RESULT:
return {
...state,
isFetching: false,
data: action.result
};
case GET_INITIAL_PRODUCTS_DATA_ERROR:
return {
...state,
isFetching: false,
error: action.error
};
case FILTER_PRODUCTS_BY_CATEGORY_ID:
return {
...state,
data: filterProductsByCategoryId(state, action.categoryId)
};
default:
return state;
}
};
export default reducer;
And there is my code to call filter action:
filterProducts = (title = "A") => {
const _categories = Object.values(this.props.categories);
const selectedCategory = _categories.find(
category => category.title === title
);
this.props.dispatch(filterProductsByCategoryId(selectedCategory.id));
My questions is:
A) Is there is a way to filter my data and display them in UI and refresh them without using ACTIONS way??
B) If A's answer is No!, How can i get my state.data and filter them in FILTER_PRODUCTS_BY_CATEGORY_ID?
Thanks.
You can use the Array.prototype.filter() to return filtered result.
keep in mind that this will return an array and not a single value, which is a good thing if you are using this filter within your reducer. because your reducer's shape is an array and not an object.
Running example:
const myData = [{
name: 'some name',
id: 1
}, {
name: 'some name2',
id: 2
}, {
name: 'some name3',
id: 3
}, {
name: 'some name4',
id: 4
}]
const filterProductsByCategoryId = (state, action) => {
return state.filter(c => c.id === action.categoryId);
};
const result = filterProductsByCategoryId(myData, {categoryId: 2});
console.log(result);
I think it is more appropriate to create a selector for a singular product that will handle this kind of action, this way you will be able to return an object instead of an array with one product in it.
Not to mention the benefits of using reselect to do some memoizations.
For this task you can use the Array.prototype.find():
const myData = [{
name: 'some name',
id: 1
}, {
name: 'some name2',
id: 2
}, {
name: 'some name3',
id: 3
}, {
name: 'some name4',
id: 4
}]
const filterProductsByCategoryId = (state, id) => {
return state.find(c => c.id === id);
};
const result = filterProductsByCategoryId(myData, 2);
console.log(result);
Im currently learning redux and trying few options out. Everything looks okay until I want to update one state in the array of objects.
I'm dispatching 5 actions in total now, first 2 setting longitutde and latitude for one part of the state using one reducer, then I set IDs with other reducer and finally when I want to update one of the objects in the array I update one but delete the other somehow.
My file looks like this:
const demoState = {
renderedDrugs: [
{
id: '',
drugId: '',
price: undefined
}
],
apt: {
latA: '',
lonA: ''
}
}
//SET_ID
const setId = (id, drugId) => ({
type: 'SET_ID',
renderedDrugs: {
id,
drugId
}
})
//SET_PRICE
const setPrice = (drugId, price) => ({
type: 'SET_PRICE',
drugId,
price
})
//RENDERED DRUGS REDUCER
const renderedDrugsReducerDefState = [];
const renderedDrugsReducer = (state = renderedDrugsReducerDefState, action) => {
switch (action.type) {
case 'SET_ID':
return [
...state,
action.renderedDrugs
]
case 'SET_PRICE':
return state.map((drug) => {
if (drug.drugId === action.drugId) {
return {
...drug,
...action.price
}
}
})
default:
return state;
}
}
//SET_LAT
const setLat = (latA) => ({
type: 'SET_LAT',
latA
})
//SET_LON
const setLon = (lonA) => ({
type: 'SET_LON',
lonA
})
//APT REDUER
const aptReducerDefState = []
const aptReducer = (state = aptReducerDefState, action) => {
switch (action.type) {
case 'SET_LAT':
return {
...state,
latA: action.latA
}
case 'SET_LON':
return {
...state,
lonA: action.lonA
}
default:
return state;
}
}
//STORE CREATION
const store = createStore(
combineReducers({
renderedDrugs: renderedDrugsReducer,
apt: aptReducer
})
)
store.subscribe(() => {
console.log('store', store.getState());
})
store.dispatch(setLat(88));
store.dispatch(setLon(78));
store.dispatch(setId(uuid(), 3));
store.dispatch(setId(uuid(), 35));
store.dispatch(setPrice(35, {price: 400}));
I assume the SET_PRICE action is at fault, but I tried various configurations and cant figure out the issue so thats why I posted the whole file, if thats unnecessary let me know and I will delete irrelevant bits.
Console log after 4th dispatch:
{renderedDrugs: Array(2), apt: {…}}
apt
:
{latA: 88, lonA: 78}
renderedDrugs
:
Array(2)
0
:
{id: "2a3c4bca-610a-4554-b7e3-695ae6e30ae7", drugId: 3}
1
:
{id: "48df057a-c8f1-4138-8db7-6268f7508ccb", drugId: 35}
length
:
2
__proto__
:
Array(0)
__proto__
:
Object
and aftr 5th
{renderedDrugs: Array(2), apt: {…}}
apt
:
{latA: 88, lonA: 78}
renderedDrugs
:
Array(2)
0
:
undefined
1
:
{id: "48df057a-c8f1-4138-8db7-6268f7508ccb", drugId: 35, price: 400}
length
:
2
__proto__
:
Array(0)
__proto__
:
Object
The .map doesn't return the unchanged objects for the items your're not updating. Adding a return should fix it:
return state.map((drug) => {
if (drug.drugId === action.drugId) {
return {
...drug,
...action.price
}
}
return drug; // this was missing before, so the return was undefined
})