Next js Redux, Objects are not valid as a React child - next.js

Error: Objects are not valid as a React child (found: object with keys {_id, name}). If you meant to render a collection of children, use an array instead.
Tried to fix this for days and no result.
i have a model
import mongoose from 'mongoose'
const CategoriesSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
parent: {
type: mongoose.Types.ObjectId,
ref: 'categories'
},
},
{
timestamps: true
})
let Dataset = mongoose.models.categories || mongoose.model('categories', CategoriesSchema)
export default Dataset
and i have getCategories like this
[getCategories ]
const getCategories = async (req, res) => {
try {
const categories = await Categories.find().populate("parent", "name");
res.json({ categories });
}
catch (err)
{
return res.status(500).json({ err: err.message });
}
};
in my Globale state i have
export const DataContext = createContext()
export const DataProvider = ({children}) => {
const initialState = {
notify: {}, auth: {}, cart: [], modal: [], orders: [], users: [], categories: []
}
const [state, dispatch] = useReducer(reducers, initialState)
useEffect(() => {
getData('categories').then(res => {
if(res.err)
return dispatch({type: 'NOTIFY', payload: {error: res.err}})
dispatch({ type: 'ADD_CATEGORIES', payload: res.categories })
})
},[])
return(
<DataContext.Provider value={{state, dispatch}}>
{children}
</DataContext.Provider>
)
}
when i call categories throw:exception
when i change dispatch in Globale state like :
dispatch({ type: 'ADD_CATEGORIES', payload: [] })
i get no elements in array :

Related

How to remain same current page pagination in redux rtk

I build an applicant with Redux RTK with createEntity
Two issue that I couldn't found it on the docs
CreateEntity is only return {ids: [], entities: []}? Is possible that return eg: totalPage from the response also?
Cache page only work on the hardcode initialState in createSlice if the pageQuery is same.
First question:
Getting the response from server was
{
users: [{id: 1}, ...]
totalPage: 100
}
I'd like to send totalPage to auto generated hook also.
export const usersAdapter = createEntityAdapter({})
export const initialState = usersAdapter.getInitialState()
export const usersApiSlice = apiSlice.injectEndpoints({
endpoints: (builder) => ({
getUsers: builder.query({
query: (args) => {
return {
url: '/api/users',
method: 'GET',
params: { page: 1, limit: 10 }
}
},
validateStatus: (response, result) => {
return response.status === 200 && !result.isError
},
transformResponse: (responseData) => {
const loadedUsers = responseData?.users.map((user) => user)
console.log("responseData: ", responseData) // <----- return { users: [], totalPage: 100 }. Could we set this totalPage value into Adapter?
return usersAdapter.setAll(initialState, loadedUsers)
},
providesTags: (result, error, arg) => {
if (result?.ids) {
return [
{ type: "User", id: "LIST" },
...result.ids.map((id) => ({ type: "User", id })),
]
} else return [{ type: "User", id: "LIST" }]
},
})
})
})
Use the hook in component
const { data } = useGetUsersQuery("page=1&limit=10");
console.log(data) // { ids: [], entity: [{}, {}] };
// expected return { ids: [], entity: [{}, {}], totalPage: 100}
Second question:
Store the page query in createSlice. The edit page will be remain same after refresh if the page query value same as initialState value.
import { createSlice } from "#reduxjs/toolkit"
const userReducer = createSlice({
name: "user",
initialState: {
query: `page=1&limit=10`,
},
reducers: {
setUserPageQuery: (state, action) => {
const query = action.payload
state.query = query
},
},
})
Page url Flow:
localhost:3000/users > localhost:3000/users/4 > refresh -> data will remain after refresh browser. (query "page=1&limit10" same as createSlice initialState value )
localhost:3000/users > localhost:3000/users/15 > refresh -> data state will gone after refresh browser. (query "page=2&limit10" different from createSlice initialState value )
Appreciate all the reply :)

converting to redux tool kit and getting "Unhandled Rejection (TypeError): state.push is not a function"

i'm stuck while converting an old project to redux tool kit getting an "Unhandled Rejection (TypeError): state.push is not a function" error. i haven't got to grips with the action/thunk and reducer immutability yet. The alerts are working but then the error msg.
import axios from 'axios';
import { setAlert } from '../alerts/alertSlice';
const slice = createSlice({
name: 'auth',
initialState: {
token: localStorage.getItem('token'),
isAuthenticated: null,
loading: true,
user: null,
},
reducers: {
registerSuccess: (state, action) => {
const { payload } = action.payload;
state.push({
payload,
isAuthenticated: true,
loading: false,
});
},
registerFail: (state, action) => {
localStorage.removeItem('token');
state.push({
token: null,
isAuthenticated: false,
loading: false,
user: null,
});
},
},
});
const { registerSuccess, registerFail } = slice.actions;
export default slice.reducer;
// Register User
export const register =
({ name, email, password }) =>
async (dispatch) => {
const config = {
headers: {
'comtent-Type': 'application/json',
},
};
const body = JSON.stringify({ name, email, password });
try {
const res = await axios.post('/api/users', body, config);
dispatch({
type: registerSuccess,
payload: res.data,
});
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
}
dispatch(registerFail());
}
};
.push is an array function to add a new item at the end of an array - your state is not an array.
You probably wanted to do something along the lines of
state.token = null
state.isAuthenticated = false
state.loading = false
state.user = null

Redux Toolkit: How to test actions with uid prepare callback

In the docs for testing incrementing todo ids, this assumes a predictable response.
In an example such as below, a unique id is generated.
How could this be tested?
This test passes, but I'm not sure if it's correct, shouldn't the id be defined based on what's in the prepare callback?
slice.js
add: {
reducer: (state, {payload}: PayloadAction<{id: string, item: Item}>) => {
state[payload.id] = payload.item
},
prepare: (item: Item) => ({
payload: {id: cuid(), item}
})
}
slice.test.js
it('should handle add', () => {
expect(
reducer(
{},
{
type: actions.add,
payload: {
id: 'id-here?',
item: {
other: 'properties...'
}
},
}
)
).toEqual({
'id-here?': {
other: 'properties...'
},
})
})
You can pull out the prepare function and also the reducer function into it's own constant and then test prepare in isolation:
todosSlice.js:
[...]
let nextTodoId = 0;
export const addTodoPrepare = (text) => {
return {
payload: {
text,
id: nextTodoId++
}
}
}
export const addTodoReducer = (state,
action) => {
const {id, text} = action.payload;
state.push({
id,
text,
completed: false
});
};
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
addTodo: {
prepare: addTodoPrepare,
reducer: addTodoReducer,
},
}
})
[...]
todosSlice.spec.js:
import todos, {addTodo, addTodoPrepare} from './todosSlice'
describe('addTodoPrepare',
() => {
it('should generate incrementing IDs',
() => {
const action1 = addTodoPrepare('a');
const action2 = addTodoPrepare('b');
expect(action1.payload).toEqual({
id: 0,
text: 'a'
})
expect(action2.payload).toEqual({
id: 1,
text: 'b'
})
})
})
describe('todos reducer',
() => {
[...]
})
For unit testing, NO, just test each reducer independently.
For integration testing and e2e testing, Yes.

testing redux async action creators with jest

I started to write tests for my application. I'm trying to create test for redux async creators.
The problem is when I run test I get following error :
Fetch all users › dispatch action loadUsersSuccess
Actions may not have an undefined "type" property. Have you misspelled a constant? Action: undefined
All actions have defined type constant so I dont understand what should be the problem.
const LOAD_ALL_USERS_SUCCESS = "src/containers/User/LOAD_ALL_USERS_SUCCESS";
const LOAD_ALL_USERS_FAILURE = "src/containers/User/LOAD_ALL_USERS_FAILURE";
//action creators
export function loadUsersSuccess(users) {
return {
type: LOAD_ALL_USERS_SUCCESS,
payload: users
};
}
export function loadUsersFailure(error) {
return {
type: LOAD_ALL_USERS_FAILURE,
payload: error
};
}
import nock from "nock";
import { loadUsersSuccess, loadUsersFailure } from "./ducks";
import configureStore from "redux-mock-store";
const middlewares = [];
const mockStore = configureStore(middlewares);
const LOAD_ALL_USERS_SUCCESS = "src/containers/User/LOAD_ALL_USERS_SUCCESS";
const LOAD_ALL_USERS_FAILURE = "src/containers/User/LOAD_ALL_USERS_FAILURE";
const users = [
{
first_name: "Emlynne",
last_name: "Spellacy",
email: "espellacy0#lycos.com",
gender: "Female",
age: 1965,
country: "Indonesia"
},
{
first_name: "Alie",
last_name: "Dalrymple",
email: "adalrymple1#telegraph.co.uk",
gender: "Female",
age: 1976,
country: "Pakistan"
}
];
function fetchData() {
return async (dispatch) => {
try {
const { data } = await axios.get("/users");
dispatch(loadUsersSuccess(data));
} catch (error) {
dispatch(loadUsersFailure(error));
}
};
}
describe("Fetch all users", () => {
afterEach(() => {
nock.cleanAll()
})
test("Should load all Users", () => {
nock("http://localhost:8000")
.get("api/users")
.reply(200, users);
const expectedAction = [
{
type: LOAD_ALL_USERS_SUCCESS,
payload: users
},
{
type: LOAD_ALL_USERS_FAILURE,
payload: "error"
}
];
const store = mockStore({});
return store.dispatch(fetchData()).then(() => {
expect(store.getActions()).toEqual(expectedAction);
});
});
});
The problem was that dispatch function didn't exist. So, I needed to add following lines.
import thunk from "redux-thunk";
const middlewares = [thunk];

How to refactor redux + thunk actions/constants

In my react/redux/thunk application I use actions like:
function catsRequested() {
return {
type: CATS_REQUESTED,
payload: {},
};
}
function catsReceived(landings) {
return {
type: CATS_RECEIVED,
payload: landings,
};
}
function catsFailed(error) {
return {
type: CATS_FAILED,
payload: { error },
};
}
export const fetchCats = () => ((dispatch, getState) => {
dispatch(catsRequested());
return catsAPI.loadCats()
.then((cats) => {
dispatch(catsReceived(cats));
}, (e) => {
dispatch(catsFailed(e.message));
});
});
To deal with some data (simplified). Everything works but i have a lot of code for every data entity (and constants too).
I mean same functions for dogs, tigers, birds etc...
I see there are similar requested/received/failed action/constant for every entity.
What is right way to minify code in terms of redux-thunk?
You can keep your code DRY by creating a types and a thunk creators:
Type:
const createTypes = (type) => ({
request: `${type}_REQUESTED`,
received: `${type}_RECEIVED`,
failed: `${type}_FAILED`,
});
Thunk:
const thunkCreator = (apiCall, callTypes) => ((dispatch, getState) => {
dispatch({ type: callTypes.request });
return apiCall
.then((payload) => {
dispatch({ type: callTypes.received, payload }));
}, (e) => {
dispatch({ type: callTypes.failed, payload: e.message }));
});
});
Now you can create a fetch method with 2 lines of code:
export const fetchCatsTypes = createTypes('CATS'); // create and export the constants
export const fetchCats = (catsAPI.loadCats, fetchCatsTypes); // create and export the thunk
export const fetchDogsTypes = createTypes('DOGS'); // create and export the constants
export const fetchDogs = (dogsAPI.loadDogs, fetchDogsTypes ); // create and export the thunk
Note: you'll also use the types constant (fetchDogsTypes) in the reducers.

Resources