Unable to assign id to items fetched from firebase database - reactjs

I am using react and redux to make a social media app. I'm storing all the data regarding posts in the firebase realtime-database. But when I fetch it I'm unable to assign firebase name property as an id to each and every post.
This is the action responsible for fetching data from firebase.
export const FetchPostStart = () => {
return {
type: actionTypes.Fetch_Post_Start
};
};
export const FetchPostSuccess = (fetchedData) => {
return {
type: actionTypes.Fetch_Post_Success,
payload: fetchedData
}
}
export const FetchPostError = (error) => {
return {
type: actionTypes.Fetch_Post_Error,
error: error
}
}
export const FetchPost = () => {
return dispatch => {
dispatch(FetchPostStart());
axios.get('/Data.json')
.then(response => {
const fetchedData = [];
for(let key in response.data){
fetchedData.push({
...response.data[key],
id: response.data.name
});
}
dispatch(FetchPostSuccess(fetchedData));
})
.catch(error => {
dispatch(FetchPostError(error));
});
}
}
This is the reducer function
case actionTypes.Fetch_Post_Start:
return {
...state,
loading:true
}
case actionTypes.Fetch_Post_Error:
return {
...state,
loading:false
}
case actionTypes.Fetch_Post_Success:
return {
...state,
loading: false,
Data: action.payload
}
The id remains undefined.
EDIT
This is how I'm trying to store id for new posts.
These are the action functions for adding a new post and to delete a post. The firebase name property is getting set as id here. But when I try to delete a post it passes a null value instead of the id.
export const NewPostSuccess = (id, postData) => {
return {
type: actionTypes.New_Post_Success,
payload: {
data: postData,
index: id
}
}
}
export const NewPostError = (error) => {
return {
type: actionTypes.New_Post_Error,
error: error
}
}
export const NewPost = (postData) => {
return (dispatch) => {
axios.post('/Data.json', postData)
.then(response => {
dispatch(NewPostSuccess(response.data.name, postData));
})
.catch(error => {
dispatch(NewPostError(error));
})
}
}
export const DeletePostSuccess = (id) => {
return {
type: actionTypes.Delete_Post_Success,
ID: id
}
}
export const DeletePost = (ID) => {
return (dispatch) => {
axios.delete('/Data/'+ ID + '.json')
.then(response => {
console.log(response.data);
dispatch(DeletePostSuccess(ID));
})
.catch(error => {
dispatch(DeletePostError(error));
})
}
}
THis is the reducer
case actionTypes.New_Post_Success:
const {Comment, ImageUrl, Date, User} = action.payload.data;
const id = action.payload.index;
console.log(id+"Reducer function")
return {
...state,
loading: false,
Data: [
...state.Data,
{Comment, ImageUrl, Date, User},
id
],
}
case actionTypes.Delete_Post_Success:
return {
...state,
loading: false,
}

.then(response => {
const fetchedData = [];
for(let key in response.data){
fetchedData.push({
...response.data[key],
id: response.data[key].name //made a small change here
});
}
dispatch(FetchPostSuccess(fetchedData));
})

Related

Why my dispatch action doesn't work in use effect after request?

I need help. I don't understand why my dispatch action doesn't work. I've redux store currency list and current currency.
My reducer:
export const currencyReducer = (
state: typeState = initialState,
action: TypeActionCurrency
): typeState => {
switch (action.type) {
case types.CURRENCY_FILL_LIST:
return { ...state, list: action.payload }
case types.CURRENCY_SET_CURRENT:
return {
...state,
current:
state.list.find(currency => currency._id === action.payload) ||
({} as ICurrency),
}
default:
return state
}
}
My actions:
export const setCurrencyList = (currencies: ICurrency[]) => ({
type: types.CURRENCY_FILL_LIST,
payload: currencies,
})
export const setCurrentCurrency = (_id: string) => ({
type: types.CURRENCY_SET_CURRENT,
payload: _id,
})
My useEffect:
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
}
}
fetchCurrencies()
}
}, [])
I want make request when load page and write currency list to Redux store, if we don't have current currency we write default currency from data.
There is one more strange thing, my redux extension shows that the state has changed, but when I receive it via the log or useSelector, it is empty
enter image description here
Thanks!
I am not 100% sure but it should work.
const [loader, setLoader] = useState(false);
const list = useSelector(state => state.list)
useEffect(() => {
if (!list.length) {
const fetchCurrencies = async () => {
try {
setLoader(true)
const data = await $apiClient<ICurrency[]>({ url: '/currencies' })
dispatch(setCurrencyList(data))
if (!current._id) dispatch(setCurrentCurrency(data[0]._id))
} catch (error) {
console.log(error)
} finally {
setLoader(false)
}
}
fetchCurrencies()
}
}, [])
useEffect(() => {
console.log(list);
}, [loader])

ReactJS | How do I get the response when the API hit process is successful?

This is function when hit api
function click to triger
const _selectBranch = async () => {
const { user, } = props;
await props.fetchGetBranchList(user.tokenResponse.accessToken, user.customerCode);
const { branchList } = props;
console.log(branchList)
}
this action dispatch
export const getBranchList = (token, code) => async (dispatch) => {
dispatch({
type: BRANCH_LIST_REQ,
});
try {
const res = await apiBranch(token).listBranchGet(code);
if(res.data != null){
dispatch({
type: BRANCH_LIST_SUCCESS,
payload: { data: res.data },
});
}
} catch (error) {
dispatch({
type: BRANCH_LIST_FAILED,
payload: error
});
}
};
my reducers
const initialState = {
branchList: null,
};
export const listNews = (state = { ...initialState }, action) => {
const { payload, type } = action;
switch (type) {
case BRANCH_LIST_REQ: {
return {
...state,
branchList: null
};
}
case BRANCH_LIST_SUCCESS: {
const { data } = payload;
return {
...state,
branchList: data
};
}
default:
return state;
}
};
I succeeded in getting a response, if I use useEffect
this code
useEffect(() => {
if(props.branchList){
setStorage("PO_ACTIVE", purchaseOrder);
props.push('/Home/BucketList/', props.branchList.cartId)
}
},[props.branchList]);
but I want to get a response in the process of this code, but if I use this code the response I get is always null
const _selectBranch = async () => {
const { user, } = props;
await props.fetchGetBranchList(user);
const { branchList } = props;
if(branchList != null ){
setStorage("PO_ACTIVE", purchaseOrder);
props.push('/Home/BucketList/', props.branchList.cartId)
}
console.log(branchList)
}

How to fetch data partially in react, redux?

Actions
import { FETCH_BLOG, FETCH_BLOG_ERROR, FETCH_BLOG_LOADING } from "../constants/blogActionTypes"
const initialState = {
blogs: [],
error: '',
loading: false,
allBlogs: []
}
// eslint-disable-next-line import/no-anonymous-default-export
export default (blogs = initialState, action) => {
switch (action.type) {
case FETCH_BLOG_LOADING:
return {
blogs: [...blogs.blogs],
loading: true,
error: ''
};
case FETCH_BLOG_ERROR:
return {
blogs: [...blogs.blogs],
loading: false,
error: action.payload
};
case FETCH_BLOG:
return {
blogs: [...action.payload, ...blogs.blogs],
loading: false,
error: ''
};
default: return blogs;
}
}
Reducers
export const fetchBlogs = (data) => async (dispatch) =>{
dispatch({ type: FETCH_BLOG_LOADING, payload: true })
fetch('http://localhost:5000/blog?show=' + data, {
method: 'GET',
headers: {
authorization: userData.token
}
})
.then(res => res.json())
.then(data => {
if (data.message) {
dispatch(fetchBlogsError(data.message))
} else {
dispatch({ type: FETCH_BLOG, payload: data })
}
})
}
React
const [fetchData, setFetchData] = useState(0);
const showData = () => {
setFetchData(fetchData + 10)
}
const dispatch = useDispatch();
const { loading, error, blogs, } = useSelector(state => state.blogs)
const getData = useCallback( () => {
dispatch(fetchBlogs(fetchData))
}, [fetchData])
useEffect(() => {
getData()
}, [getData])
On the first render, I fetch 10 items.after clicking on load more I fetch another 10 data from database. On the blog component it's fine but after go back to the home page and get back to the blog page; the blog items duplicates. How to fix this duplicate issue>
There are two issues here which are inter-related, you possibly don't need to address #2 depending on how you address #1.
You should add a condition to your thunk action so that you don't fetch a page that you have previously fetched.
You should separate your blog items by page so that you aren't always appending the newest items at the end of the array if you fetch page 1 twice.
Sidenote: [...blogs.blogs] is unnecessary because there is reason to clone properties which you aren't changing.
I'm confused by your API calls. It looks like /blog?show=20 is getting posts 21-30 but I would think based on the name show that it would be posts 1-20.
Using position indexes:
import { createAsyncThunk, createReducer } from "#reduxjs/toolkit";
export const fetchBlogs = createAsyncThunk(
"blogs/fetchBlog",
async (startIndex, { getState, rejectWithValue }) => {
const res = await fetch("http://localhost:5000/blog?show=" + startIndex, {
method: "GET",
headers: {
// where does userData come from ??
authorization: userData.token
}
});
const data = await res.json();
if (data.message) {
rejectWithValue(data.message);
} else {
return data;
}
},
{
condition: (startIndex, { getState }) => {
const { blogs } = getState();
// cancel if loading of if first post on paage is loaded
if (blogs.loading || blogs.blogs[startIndex]) {
return false;
}
}
}
);
const initialState = {
blogs: [],
error: "",
loading: false
};
export default createReducer(initialState, (builder) =>
builder
.addCase(fetchBlogs.pending, (state) => {
state.loading = true;
state.error = "";
})
.addCase(fetchBlogs.rejected, (state, action) => {
state.loading = false;
state.error = action.payload ?? action.error;
})
.addCase(fetchBlogs.fulfilled, (state, action) => {
const startIndex = action.meta.arg;
const newBlogs = action.payload;
// insert in the array at the correct position
state.blogs.splice(startIndex, newBlogs.length, newBlogs);
})
);
Using separated pages:
import { createAsyncThunk, createReducer, createSelector } from "#reduxjs/toolkit";
export const fetchBlogs = createAsyncThunk(
"blogs/fetchBlog",
async (pageNumber, { getState, rejectWithValue }) => {
const startIndex = 10 * (pageNumber - 1);
const res = await fetch("http://localhost:5000/blog?show=" + startIndex, {
method: "GET",
headers: {
// where does userData come from ??
authorization: userData.token
}
});
const data = await res.json();
if (data.message) {
rejectWithValue(data.message);
} else {
return data;
}
},
{
condition: (pageNumber, { getState }) => {
const { blogs } = getState();
// cancel if loading of if there is a property for this page
if (blogs.loading || blogs.blogs[pageNumber]) {
return false;
}
}
}
);
const initialState = {
//arrays keyed by page number
blogs: {},
error: "",
loading: false
};
export default createReducer(initialState, (builder) =>
builder
.addCase(fetchBlogs.pending, (state) => {
state.loading = true;
state.error = "";
})
.addCase(fetchBlogs.rejected, (state, action) => {
state.loading = false;
state.error = action.payload ?? action.error;
})
.addCase(fetchBlogs.fulfilled, (state, action) => {
const pageNumber = action.meta.arg;
state.blogs[pageNumber] = action.payload;
})
);
// want to flatten the blogs array when selecting
// create a memoized selector
export const selectBlogs = createSelector(
state => state.blogs,
(blogsState) => ({
...blogsState,
blogs: Object.values(blogsState.blogs).flat(1)
})
)
With component:
export default () => {
const [pageNumber, setPageNumber] = useState(1);
const showNext = () => {
setPageNumber((page) => page + 1);
};
const dispatch = useDispatch();
const { loading, error, blogs } = useSelector(selectBlogs);
useEffect(() => {
dispatch(fetchBlogs(pageNumber));
}, [dispatch, pageNumber]);

How can I wait until I get the dispatch result in React

consider the following code :
const onSubmit = (data) => {
dispatch(Actions.updatedUser(data))
navigation.navigate('xxxx')
}
When I call Submit function we want to wait until finishing dispatch then navigate , Here How can I do that ?
This is my action :
export const updatedUser = (model) => {
return dispatch => {
api
.patch("/xxx")
.then(response => { return response.data['data'] })
.then(result => {
dispatch({ type: Actions.AUTH_UPDATE_USER, payload: result })
})
.catch(error => { })
}
}
my reducer :
const initState = {
userInfo: undefined
}
export default (state = initState, action) => {
switch (action.type) {
case Actions.AUTH_UPDATE_USER:
return { ...state, userInfo: action.payload }
default:
return state;
}
}
Here's what you can do: from your action, you can return a promise which resolves only when the dispatch is completed. Something like this:
export const updatedUser = (model) => {
return dispatch => {
return new Promise((resolve, reject) => {
api
.patch("/xxx")
.then(response => { return response.data['data'] })
.then(result => {
dispatch({ type: Actions.AUTH_UPDATE_USER, payload: result })
resolve() // <<<< this!
})
.catch(error => { reject() })
})
}
}
Now, in your component code, you can either do .then or async/await based on your preference. Here's how it would look with then:
const onSubmit = (data) => {
dispatch(Actions.updatedUser(data)).then(() => {
navigation.navigate('xxxx')
})
}
Here's a sandbox for an example

redux handling fetch response data and update store

I'm trying to migrate to redux-thunk I have few issues here.
Previously, I had fetch response data which is saved in state, and with that state I used filter to save other states in the app.
loadMeetingRoomData = async () => {
try {
const getMeetingRoomData = await fetch(URL, { headers: myHeaders });
const responseJson = await getMeetingRoomData.json();
if (responseJson.length === 0) {
this.setState({
meetingRoomStatus: false,
})
} else {
this.setState({
meetingRoomData: responseJson,
});
const inProgressMeeting = responseJson.filter(obj => {
return obj.Status === INPROGRESS_CODE
});
this.setState({
meetingRoomInProgressCount: inProgressMeeting.length,
});
const upcomingMeeting = responseJson.filter(obj => {
return obj.Status === UPCOMING_CODE_MORE_THAN_30_MIN || obj.Status === UPCOMING_CODE_LESS_THAN_30_MIN;
});
this.setState({
meetingRoomUpcomingCount: upcomingMeeting.length,
})
const finishedMeeting = responseJson.filter(obj => {
return obj.Status === FINISHED_CODE
})
this.setState({
meetingRoomFinishedCount: finishedMeeting.length,
})
}
}
catch (err) {
console.log(new Error(err));
}
}
And below is the code, My question is where do I filter the reponseJson to
update the new three states in the previous code ( meetingRoomInProgressCount, meetingRoomUpComingCount, meetingRoomFinished)
Do I have to dispatch it in the component file using mapDispatchToProps or
use middleware to update the store , or filter inside the reducer?
#Action Creator
export const fetchMeetingRoom = () => {
return function(dispatch){
return fetch(URL, { headers: myHeaders })
.then(response => response.json())
.then(json =>
dispatch({ type: 'FETCH_MEETINGROOM_SUCCESS' ,payload : json})
)
}
}
export default function reducer( state = initialState , action){
switch(action.type) {
case FETCH_MEETINGROOM_SUCCESS :
let value = action.payload;
if(value.length === 0){ return {...state,meetingRoomStatus :false,}}
else{
return {...state,
isLoading : false,
meetingRoomData : action.payload}
}
default :
return state;
}
}
I use this structure for my projects
actions:
import { TYPE_VARS } from "./type";
export const func_name = () => (dispatch) => {
return fetch(URL, {
method: "GET",
headers: {
"Content-Type": "application/json",
}
})
.then((res) => res.json())
.then((data) => {
dispatch({
type: TYPE_VARS,
payload: data,
});
return data;
})
.catch((err) => console.log(err.message));
};
reducer:
import { TYPE_VARS } from "../actions/type";
const initialState = {
my_state: {},
};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case TYPE_VARS:
return {
...state,
my_state: payload,
};
default:
return state;
}
}
component:
// call action where do you need
func_name()
const mapStateToProps = state => ({
my_state: state.reducerFileName.my_state
})
export default connect(mapStateToProps, { func_name })(ComponentName)

Resources