Retrieved data from firebase v9 is undefined - javascript

When retrieving data and console.log it, the data shows perfectly, but when trying to dispatch the action with the argument as a data it turns out to be undefined.
I tried to use await before dispatch the action, but it didn't change anything. Why does it happen?
actions.js
import * as types from './actionTypes'
import { db } from '../firebase';
import { collection, getDocs } from "firebase/firestore";
const getFeedbacksStart = () => ({
type: types.GET_FEEDBACKS_START,
});
const getFeedbacksSussess = (feedbacks) => ({
type: types.GET_FEEDBACKS_SUCCESS,
payload: feedbacks
});
const getFeedbacksFail = () => ({
type: types.GET_FEEDBACKS_FAIL,
});
export const getFeedbacks = () => {
return async function (dispatch) {
dispatch(getFeedbacksStart());
try {
const querySnapshot = await getDocs(collection(db, "feedbacks"));
querySnapshot.forEach((doc) => {
console.log(doc.id, " => ", doc.data())
});
const feedbacks = querySnapshot.forEach((doc) => doc.data());
dispatch(getFeedbacksSussess(feedbacks))
} catch (error) {
dispatch(getFeedbacksFail(error))
}
}
}
actionTypes.js
export const GET_FEEDBACKS_START = 'GET_FEEDBACKS_START';
export const GET_FEEDBACKS_SUCCESS = 'GET_FEEDBACKS_SUCCESS';
export const GET_FEEDBACKS_FAIL = 'GET_FEEDBACKS_FAIL';
reducer.js
import * as types from './actionTypes'
const initialState = {
feedbacks: {},
loading: false,
error: null,
};
const feedbackReducer = (state = initialState, action) => {
switch (action.type) {
case types.GET_FEEDBACKS_START:
return {
...state,
loading: true
}
case types.GET_FEEDBACKS_SUCCESS:
return {
...state,
loading: false,
feedbacks: action.payload,
}
case types.GET_FEEDBACKS_FAIL:
return {
...state,
loading: false,
error: action.payload,
}
default:
return state;
}
}
export default feedbackReducer;
root-reducer.js
import { combineReducers } from "redux";
import feedbackReducer from "./reducer";
const rootReducer = combineReducers({
data: feedbackReducer,
});
export default rootReducer;
store.js
import { configureStore } from '#reduxjs/toolkit';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import rootReducer from './root-reducer';
const store = configureStore({
reducer: rootReducer,
middleware: [thunk, logger],
});
export default store;
ListRecord.js where I dispatch the action
import React, { useEffect, useState, useContext } from "react";
import { useSelector, useDispatch } from 'react-redux';
import { getFeedbacks } from "../redux/actions";
const ListRecord = () => {
const [data, setData] = useState({});
console.log("data", data);
const state = useSelector(state => state.data);
console.log("state =>", state);
let dispatch = useDispatch();
useEffect(() => {
dispatch(getFeedbacks());
}, [])
return (
<>
</>
);
};
export default ListRecord;

I figured out what I was doing wrong. I tried to retrieve the data in the wrong way. I was trying to use forEach method on a collection. Firstly, it needed to refer to the docs inside a db -> querySnapshot.docs and then you can use .map() method and loop through the whole collection you have inside your database.
The example of how to do it right with firebase v9 is HERE
Here is a working code :)
In actions.js
export const getFeedbacks = () => {
return function (dispatch) {
dispatch(getFeedbacksStart())
const getData = async () => {
try {
const querySnapshot = await getDocs(collection(db, "feedbacks"));
const feedbacks = querySnapshot.docs.map((doc) => ({
...doc.data(),
id: doc.id
}))
dispatch(getFeedbacksSussess(feedbacks));
} catch (error) {
dispatch(getFeedbacksFail(error))
}
}
getData();
}
}

Related

Problems with createAsyncThunk

I have just completed Learn Redux on Codecademy and want to that knowledge in practice. But I have an error. When I create extraReducers for updating the state to actual promise status it does not add information.
getUserSlice.js
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchUserInfo } from '../../api';
export const loadUser = createAsyncThunk("getUser/loadUser",
async (arg, thunkAPI) => {
return await fetchUserInfo();
}
});
const sliceOptions = {
name: 'getUser',
initialState: {
info: [],
isLoading: false,
hasError: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loadUser.pending, (state) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(loadUser.fulfilled, (state, action) => {
state.info.push(action.payload)
state.isLoading = false;
state.hasError = false;
})
.addCase(loadUser.rejected, (state, action) => {
state.isLoading = false;
state.hasError = true;
})
},
};
export const getUserSlice = createSlice(sliceOptions);
console.log(getUserSlice);
export const selectUserInfo = (state) => {
console.log(state);
return state;
};
export default getUserSlice.reducer;
api.js
export const fetchUserInfo = async () => {
const user = await fetch('http://localhost:5000/api/user');
const json = user.json();
return json;
}
App.js
import React from 'react';
import './App.css';
import {Container} from 'react-bootstrap';
import Achievement from './components/Achievement/Achievement';
import { useSelector } from 'react-redux';
import { selectUserInfo } from './features/getUser/getUserSlice';
const colors = ['#010626','#4d6396', '#5d1a87', '#5d1a87', '#5d1a87'];
function App() {
let color= colors[0];
const user = useSelector(selectUserInfo)
function changeColor() {
const newColor = `rgb(${Math.round(Math.random() *256)}, ${Math.round(Math.random() *256)}, ${Math.round(Math.random() *256)})`;
color = newColor;
}
return (
<div className="App" style={{ background: color }}>
<Container>
<h1 id="whoAmI">
Witaj na moim portfolio
{user}
</h1>
<button onClick={changeColor}>
Zmień kolor tła
</button>
<div className="col-lg-4 col-md-6 col-sm-12">
<Achievement title="Ukończenie The Web Developer Bootcamp" picture="https://res.cloudinary.com/syberiancats/image/upload/v1630317595/k9g0nox2fyexxawg8whu.jpg" />
</div>
</Container>
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux';
import reportWebVitals from './reportWebVitals';
import { store } from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
reportWebVitals();
store.js
import { configureStore } from "#reduxjs/toolkit";
import getUserReducer from "./features/getUser/getUserSlice";
export const store = configureStore({
reducer: {
getUser: getUserReducer
}
})
Console.log of getUserSlice and state in the selector
Maybe you can use (builder) => {} function in extraReducer and you edit your loadUser because your Api.js already return json like code below:
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import { fetchUserInfo } from '../../api';
export const loadUser = createAsyncThunk('getUser/loadUser', async (arg, thunkAPI) => {
return await fetchUserInfo();
});
const sliceOptions = {
name: 'getUser',
initialState: {
info: [],
isLoading: false,
hasError: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(loadUser.pending, (state) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(loadUser.fulfilled, (state, action) => {
state.info.push(action.payload)
state.isLoading = false;
state.hasError = false;
})
.addCase(loadUser.rejected, (state, action) => {
state.isLoading = false;
state.hasError = true;
})
},
};
export const getUserSlice = createSlice(sliceOptions);
console.log(getUserSlice);
export const selectUserInfo = (state) => {
console.log(state);
return state;
};
export default getUserSlice.reducer;
and you maybe forgot to add await before fetch, you should edit your fetching data in api.js into this below:
export const fetchUserInfo = async () => {
const user = await fetch('http://localhost:5000/api/user');
const json = user.json();
return json;
}
You can implicitly return and bypass the Promise result. Something like:
(arg, thunkAPI) => fetchUserInfo();
However, I would take the "by-the-book" way:
export const loadUser = createAsyncThunk("getUser/loadUser",
async (arg, tunkApi) => {
try {
const response = await fetchUserInfo();
return response;
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}
});

Reducer can't read action.type REDUX

I'm new to redux and I can't figure out what I'm doing wrong
accessTokenActions.js file
import { getAccessToken } from '../../utils/spotifyAuth';
import * as types from '../consts/types';
export const fetchAccessTokenRequest = () => ({
type: types.FETCH_ACCESS_TOKEN,
});
export const fetchAccessTokenSuccess = (data) => ({
type: types.FETCH_ACCESS_TOKEN_SUCCESS,
payload: {
data,
},
});
export const fetchAccessTokenError = (error) => ({
type: types.FETCH_ACCESS_TOKEN_ERROR,
payload: {
error,
},
});
export const fetchAccessToken = () => async (dispatch) => {
dispatch(fetchAccessTokenRequest());
try {
const response = await getAccessToken();
if (!response) {
throw Error();
}
return dispatch(fetchAccessTokenSuccess(response.data));
} catch (err) {
return dispatch(fetchAccessTokenError(err.response.data));
}
};
accessTokenReducer.js file
import * as types from '../consts/types';
const initialState = {
accessToken: null,
isLoading: false,
error: null,
};
const accessTokenReducer = (state = initialState, action) => {
switch (action.type) {
case types.FETCH_ACCESS_TOKEN:
return {
...state,
isLoading: true,
};
case types.FETCH_ACCESS_TOKEN_SUCCESS:
return {
...state,
accessToken: action.payload.access_token,
};
case types.FETCH_ACCESS_TOKEN_ERROR:
return {
...state,
accessToken: null,
isLoading: false,
error: action.payload.error,
};
default:
return state;
}
};
export default accessTokenReducer;
index.js file
import { combineReducers } from 'redux';
import accessTokenReducer from './accessTokenReducer';
const rootReducer = combineReducers({
accessToken: accessTokenReducer,
});
export default rootReducer;
configureStore.js file
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/index';
const configureStore = () => {
const middlewares = [thunk];
const store = createStore(
rootReducer,
compose(
applyMiddleware(...middlewares),
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__(),
),
);
return store;
};
export default configureStore;
const/types file
export const FETCH_ACCESS_TOKEN = 'FETCH_ACCESS_TOKEN';
export const FETCH_ACCESS_TOKEN_SUCCESS = 'FETCH_ACCESS_TOKEN_SUCCESS';
export const FETCH_ACCESS_TOKEN_ERROR = 'FETCH_ACCESS_TOKEN_ERROR';
I also have a selectors file(which I think doesn't affect the code cause I don't call any functions from there)
I keep getting TypeError: Cannot read property 'type' of undefined error in my switch statement at the reducer function, I'm currently studying the redux documentation but I can't figure out what's going on
Any help would be greatly appreciated

How to dispatch in .then() in react-redux

I am working on project where I am stuck in this problem. The thing is, I am calling an axios API and after its success I want to update my redux state i.e. in the .then() chain of axios. How can I achieve that? As what I have tried by applying what I know is -> I have created a react-redux dispatch in my component. I know how to do this in normal onClick but in then method I don't know how to trigger that.
I have tried doing this:
let submitForm = (e) => {
e.preventDefault();
// Axios request
const url = 'http://localhost:5000/api/v1/users/login'
axios({
//Api details
})
.then(res => {
// Store API data in LocalStorage
})
.then(() => {
LogIN(); // Here I want to change redux state //
history.push('/dashboard')
})
}
--Component
function Signin({LogIN}) {
return (
)
}
const mapDispatchToProps = dispatch => {
return {
LogIN: () => dispatch(login_action())
}
}
export default connect(null , mapDispatchToProps)(Signin)
After doing this, I see same state with no difference
Here is redux:
const login_action = () => {
return {
type : 'LOG-IN'
}
}
const loginLogOutReducer = (state = false, action) => {
switch (action.type) {
case 'LOG_IN':
return !state
default:
return state
}
}
const AllReducers = combineReducers({
isLoggedIn : loginLogOutReducer
})
You can use redux-thunk and function component in react hook
App.js
import {Provider} from 'react-redux'
import store from './store'
<Provider store={store()}>
<AppComponent />
</Provider>
store.js
import {applyMiddleware, compose, createStore} from 'redux'
import thunk from 'redux-thunk'
import {initialState, rootReducer} from './reducers'
const store = () => {
return createStore(rootReducer, initialState, compose(applyMiddleware(thunk)))
}
export default store
reducer.js
import {actionTypes} from './actionTypes'
const initialState = {}
const rootReducer = (state = initialState, action) => {
if (action.type === actionTypes.STH) {
return {
...state,
sth: action.payload,
}
}
}
export {initialState, rootReducer}
actionTypes.js
export const actionTypes = {
STH: 'STH'
}
Component
...
const onChange = => {
dispatch(actionFunc()).then(res => {
// DO Something
})
...
action.js
const actionFunc = () => {
return (dispatch, getState) => {
return axios({
//Api details
}).then(res => res).catch(err => err)
}
}

Redux state does not update even when reducer called with correct data

For some reason, even though the reducer runs and console.log shows that the correct data was passed to it, the redux store was not updated.
Relevant files:
App.jsx
import {Provider} from 'react-redux';
import store from './store';
const Stack = createStackNavigator();
export default class App extends Component {
render() {
return (
<Provider store={store()}>
Store.js
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk';
const store = (initialState = {}) =>{
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
)
}
export default store;
Register.tsx
...
<Pressable
style={styles.button}
onPress={() => this.props.submitRegistration(this.state)}
>
...
const mapDispatchToProps = (dispatch: any) => {
return {
submitRegistration: (data: any) => {
dispatch(UserActions.submitRegister(data))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
UserActions
import { signUp } from '../../services/backend';
import { setUser } from '../../actions';
export function submitRegister(data: any) {
return async (dispatch: any) => {
const response = await signUp(data);
const responseData = await response.json();
if(responseData.token) {
console.log('here', responseData);
dispatch(setUser(responseData.user));
}
};
}
Action creator
export const setUser = (user: any) => ({
type: 'SET_USER',
user
});
User Reducer
import { SET_USER } from "../actions/actionTypes"
const initialState = {
user: {}
}
const User = (state = initialState, action: any) => {
switch(action.type) {
case SET_USER:
console.log('here action', action.user);
return { user: action.user}
default:
return state
}
}
export default User;
I would really appreciate any help possible. Seems like I misconfigured in someway because even when I set initial state :
const initialState = {
user: {firstName: "John"}
}
it's not reflected in the redux store.
In your action creator:
export const setUser = (user: any) => (
return {
type: 'SET_USER',
user
});

react redux and combine reducers

Hello I am using thunks to get data from my backend
but I am unsure how to do it in my combine reducer
my types:
export const FETCH_SUCESS = 'FETCH_SUCESS';
export const FETCH_FAIL = 'FETCH_FAIL';
export const FETCH_LOADING = 'FETCH_FAIL';
export const FILTER_PRODUCT = 'FILTER_PRODUCT';
my action:
import api from '../../services/api';
import {FETCH_SUCESS,FETCH_FAIL,FETCH_LOADING} from '../constants/fetchTypes';
const fetchSucess = data => ({
type: FETCH_SUCESS,
payload: {
...data
}
});
const fetchStarted = () => ({
type: FETCH_LOADING
});
const fetchFailed = error => ({
type: FETCH_FAIL,
payload: {
error
}
});
export const fetchProduct = () => {
console.log('action')
return dispatch => {
dispatch(fetchStarted());
api
.get('/products')
.then(res => {
dispatch(fetchSucess(res.data));
})
.catch(err => {
dispatch(fetchFailed(err.message));
});
};
};
my reducer:
import {
FETCH_SUCESS,
FETCH_FAIL,
FETCH_LOADING,
} from '../constants/fetchTypes';
const initialState = {
loading: false,
data: [],
error: null
};
export default function productReducer(state = initialState, action) {
switch (action.type) {
case FETCH_LOADING:
return {
...state,
loading: true
};
case FETCH_SUCESS:
return {
...state,
loading: false,
error: null,
data: [...state.data, action.payload]
};
case FETCH_FAIL:
return {
...state,
loading: false,
error: action.payload.error
};
default:
return state;
}
}
my combiner:
import { combineReducers } from 'redux'
import productReducer from './productsFetch.reducer';
export default combineReducers({
});
my store:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
my home.js
class HomeProducts extends Component {
componentDidMount() {
this.props.fetchData();
}
render() {
const productItems = this.props.products.map( product => (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
)
return (
<div className="container">
<div className="row">
{productItems}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
console.log(state);
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch(fetchProduct())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
I have doubt what to use in my combiner
to get the date and the mistakes How I have my loading,data, error
I don't know how I will do it in meu combine redux
I also don't know if I had the best practices in my action and my reducer
In your combiner file just add your reducers as key value pairs like so:
import { combineReducers } from 'redux'
import productReducer from './productsFetch.reducer';
// import anotherReducer from './yourPath';
export default combineReducers({
products: productReducer,
// anotherState: anotherReducer
});
Ideally you should import your actions and pass it your component through your connect method like so then you will be able to access it from your component as props.
import fetchProduct from './pathToYourActionFile';
const mapStateToProps = (state) => {
console.log(state);
};
const mapActionsToProps = {
fetchProduct: fetchProduct
};
export default connect(mapStateToProps, mapActionsToProps)(HomeProducts);
import thunkInject from 'redux-thunk-inject';
const mockStore = configureMockStore([thunkInject()]);
const store = mockStore(mockStore);
const wrapper = mount(<Provider store={store} />);
expect(wrapper).toMatchSnapshot();
to mock a store with thunk, you can inject it as a prop in a component. Or in a reducer, e.g.
import productReducer from '../productReducer';
import {
FETCH_SUCESS,
FETCH_FAIL,
FETCH_LOADING,
} from '../constants/fetchTypes';
describe('product reducer', () => {
it('Should handle FETCH_SUCCESS', () => {
expect(productReducer(store, FETCH_SUCCESS)
).toEqual({
loading: true
});
expect(productReducer(store, FETCH_FAIL).toEqual({
loading: false,
error: action.payload.error})
});

Categories