Hello I have basic code with useSelector, where data from the useEffect is stored into currentItems.
How to iterate through the state array and render it?
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getToDoItems, setItems } from "../redux/globalSlice";
const ToDoList = () => {
const dispatch = useDispatch();
const { currentItems } = useSelector((state) => state.global);
useEffect(() => {
async function getToDoItems(id) {
const response = await fetch(
`https://xxx.mockapi.io/api/list/${id}/todos`
);
const data = await response.json();
dispatch(setItems(data));
}
getToDoItems(2);
}, []);
return (
<div>
<h1>ahoj</h1>
{currentItems.map((todo) => {
<div>{todo.title}</div>;
})}
</div>
);
};
export default ToDoList;
I understand that this is an asynchronous operation, but I don't know how to render it until after loading and storing data to store from the api.
Related
I have this in postsSlice
export const getPosts = createAsyncThunk(
'posts/getPosts',
async (thunkAPI)=> {
const response = await api.fetchPosts()
// const response = await fetch('http://localhost:3002/api/posts').then(
// (data) => data.json()
// )
return response.data
})
and commented response works fine but const response = await api.fetchPosts() doesn't work properly, it sends me errors that posts.map is not a function but data Arrays looks fine
api.fetchPosts() looks like this
import axios from "axios"
const API = axios.create({ baseURL: "http://localhost:3002/api"})
API.interceptors.request.use((req) => {
if (localStorage.getItem('access_token')) {
req.headers.Authorization = `Bearer ${
JSON.parse(localStorage.getItem('access_token')).token
}`
}
return req
})
export const fetchPosts = () => API.get(`/posts`)
here is where map function is
import React, {useEffect, useState} from 'react';
import {useDispatch, useSelector} from "react-redux";
import CardMain from "../../components/CardMain/CardMain";
import {createPost, getPosts} from "../../redux/features/posts/postsSlice"
const Main = () => {
const dispatch = useDispatch()
const {posts} = useSelector((state) => state.posts)
useEffect(() => {
dispatch(getPosts())
}, []);
console.log(posts)
return (
<div>
<div>
{
posts.map(user=>(
<CardMain post={user} key={user._id}/>
))
}
</div>
</div>
);
};
export default Main;
In your console.log you can see, that posts is actually an object in the form { data: [ .... ] } - you cannot .map on an object.
So either you need to return response.data.data in your thunk, or do posts.data.map(... instead of posts.map(... (or change your api method to directly return correct unnested data)
Hello i am new to programming, and i have learn to use MERN to make a sign up,i have no issue with backend but when i tried to use redux ,i have this problem in the frontend
enter image description here
This is the code for the SignInScreen.js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
I dont know what wrong with the code,but i do know that it connected with the redux store which have reducer,action and constant..this is for the redux store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import {
userLoginReducer,
} from './reducers/userReducer'
const userInfo = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
const initialState = {
userLogin: { userInfo },
}
const reducer = combineReducers({
userLogin: userLoginReducer,
})
const middleware = [thunk]
const store = createStore(
reducer,
initialState,
compose(applyMiddleware(...middleware))
)
export default store
This is for constant
export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL'
export const USER_LOGOUT = 'USER_LOGOUT'
this is userReducer.js
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
USER_LOGOUT,
} from '../constants/userConstant'
function userLoginReducer(state = {}, action) {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true }
case USER_LOGIN_SUCCESS:
return { loading: false, userInfo: action.payload }
case USER_LOGIN_FAIL:
return { loading: false, error: action.payload }
case USER_LOGOUT:
return {}
default:
return state
}
}
export userLoginReducer
and lastly for user.js
import Axios from 'axios'
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
} from '../constants/userConstant'
const login = (email, password) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST })
const config = { headers: { 'Content-Type': 'application/json' } }
const { data } = await Axios.post(
'/api/users/login',
{ email, password },
config
)
dispatch({ type: USER_LOGIN_SUCCESS, payload: data })
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
export login
I just want to get the data into the userInfo but it dont recognized them and said it is a TypeError..I hope u can help me with this..im using the redux latest version
The problem is in userLoginReducer. Each reducer should return a complete new copy of the store.
If you return just the changes, the object you return replaces the entire state.
For example in this code:
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true };
}
The state { userLogin: { userInfo } } will be replaced with { loading: true }. Then you will not have userLogin anymore in the state. That's why you get the error.
To overcome this problem, spread the previous state in returned object (for all actions):
switch (action.type) {
case USER_LOGIN_REQUEST:
return { ....state, loading: true }; // ...state copies exist state to the new copy of state
}
Note: To easily solve similar bugs in the future, I recommend to use redux devtools extension. It is a great extension for debugging and look at changes in redux store.
I have tried that but still cannot fix my problem,this is the rest of my signinScreen,js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}
it still show the same error like before,i have no idea to solve this
I have an AuthContext to manage the authentication in my Next.js app. The _app.js file looks like this:
import '../styles/global.css'
import 'tailwindcss/tailwind.css'
import { AuthProvider } from '../components/AuthContext'
function MyApp({ Component, pageProps }) {
return (
<>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
</>
)
}
export default MyApp
And the AuthContext file is something like this:
import React, { useContext, useState, useEffect } from "react"
import { auth, db } from "./Firebase";
import { doc, getDoc } from "firebase/firestore";
const AuthContext = React.createContext()
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState([])
const [loading, setLoading] = useState(true)
const [userData, setUserData] = useState({})
const [companyData, setCompanyData] = useState({})
useEffect(() => {
const unsubscribe =
auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
useEffect(() => {
const fetchData = async () => {
const qu = doc(db, "users", currentUser.uid)
const datau = await getDoc(qu)
setUserData(datau.data());
const qc = doc(db, "companies", currentUser.uid)
const datac = await getDoc(qc)
setCompanyData(datac.data());
}
currentUser?.uid && fetchData()
}, [currentUser])
const value = {
currentUser,
userData,
companyData
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
export function useAuth() {
return useContext(AuthContext)
}
Then I have a dynamic page [id].js that is something like this:
import { useAuth } from '../../components/AuthContext';
import Head from 'next/head';
export async function getStaticProps(context) {
// fetch and return data
}
export async function getStaticPaths() {
// fetch paths
return { paths, fallback: 'blocking' }
}
export default function Job(props) {
const { userData, currentUser } = useAuth();
const data = props.data;
return (
<>
<Head>
{data.title} | SiteName
</Head>
<h1>data.title</h1>
{currentUser ? userData.name : null}
</>
)
}
Problem: The website works perfectly, BUT when I check the page [id].js source code, Next.js doesn't build the HTML structure with head and body optimized for SEO (which is the main reason why I'm migrating to Next.js).
If I remove the "!loading &&" (see below) in the AuthContext file, the Next's SSG HTML generation works BUT the whole app starts to give me errors everywhere.
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
What exactly is the problem? And any idea on how to solve it?
Im getting data from the external api and storing it in the reducer.And im using redux-persist to persist the state while navigating from one page to another.But i have made left the whiteList as an empty array but all the state are being persisted?Need help
import "../assets/css/style.scss";
import "owl.carousel/dist/assets/owl.carousel.css";
import "owl.carousel/dist/assets/owl.theme.default.css";
import Layout from "../component/Layout/Layout";
import { wrapper } from "../redux/store";
import { useEffect } from "react";
import { useStore } from "react-redux";
function MyApp({ Component, pageProps }) {
const store = useStore((store) => store);
useEffect(() => {
{
typeof document !== undefined
? require("bootstrap/dist/js/bootstrap.bundle")
: null;
}
}, []);
return (
<Layout>
<Component {...pageProps} />;
</Layout>
);
}
export default wrapper.withRedux(MyApp);
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "./index";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const middleware = [thunk];
let initialState={}
// BINDING MIDDLEWARE
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== "production") {
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const makeStore = ({ isServer }) => {
if (isServer) {
//If it's on server side, create a store
return createStore(rootReducer,initialState, bindMiddleware(middleware));
} else {
//If it's on client side, create a store which will persis
const persistConfig = {
key: "root",
storage: storage,
whiteList: [],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer,initialState, bindMiddleware(middleware));
store.__persisitor = persistStore(store); // This creates a persistor object & push that
persisted object to .__persistor, so that we can avail the persistability feature
return store;
}
};
// export an assembled wrapper
export const wrapper = createWrapper(makeStore);
If you keep the whitelist an empty array then nothing will be persisted.
You have to put inside that string array the redux reducers values you want to be persisted.
Example:
const persistConfig = {
key: 'root',
storage: storage,
whiteList: ['cart', 'form', 'user'],
};
I am making a cart functionality using redux toolkit's createSlice. But in the browser's application tab, the value of the localStorage is showing as [object Object]. Can someone help me with this please?
cartSlice.js
import { createSlice } from '#reduxjs/toolkit';
import axios from 'axios'
const cartItemsFromStorage = localStorage.getItem('cartItems') ? localStorage.getItem('carts') : []
export const cartSlice = createSlice({
name: 'cart',
initialState: {
cartItems: cartItemsFromStorage,
},
reducers: {
add: (state, action) => {
const item = action.payload
const existItem = state.cartItems.find(x => x.product === item.product)
if (existItem) {
const currentItems = state.cartItems.map(x => x.product === existItem.product ? item : x)
state.cartItems = [...currentItems]
} else {
state.cartItems = [...state.cartItems, item]
localStorage.setItem('cartItems', state.cartItems)
}
},
// remove: (state, action) => {
// },
},
});
const { add } = cartSlice.actions;
export const selectCartItems = state => state.cart.cartItems;
export const addToCart = (id, qty) => async (dispatch) => {
const { data } = await axios.get(`/api/products/${id}`)
dispatch(add({
product: data._id,
name: data.name,
imgae: data.imgae,
price: data.price,
countInStock: data.countInStock,
qty
}))
}
export default cartSlice.reducer;
CartScreen.js
import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { selectCartItems, addToCart } from '../features/cartSlice'
const CartScreen = ({ match, location, history }) => {
const productId = match.params.id
const qty = location.search ? Number(location.search.split('=')[1]) : 1
const dispatch = useDispatch()
const cartItems = useSelector(selectCartItems)
useEffect(() => {
if (productId) {
dispatch(addToCart(productId, qty))
}
console.log(`cartItems: ${cartItems}`)
}, [dispatch, productId, qty])
return (
<div>
Cart
</div>
)
}
export default CartScreen
The next time I refresh the page, the initialState is not there, insted it shows [object Object]. I know the problem is with localStorage. Please correct me.
I think the problem is you are doing localStorage stuff in a reducer action. Reducer only can do simple operations and modify the state, so I encourage you to try to pass localStorage calls into the thunk action.