I have a list of orders, I'm using FlatList to render them,
I have 2 main concepts should I have "Load More, Pull to Refresh"
In my case user can edit the order then pull to refresh to get the newly updated data, and another case for load more "pagination".
SO
Load more functionality work properly well, and gets the new data from the server and pushed to the state with the old data,
BUT pull to refresh not works, it's not updated the data when setState, although the function that gets data triggered, and I can see the updated data in the console, It just renders the old data!
Here's a code snippet.
const OpenedAppointments = () => {
const [openedAppointment, setOpenedAppointment] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [lastPage, setLastPage] = useState(null);
const [loading, setLoading] = useState(false);
const [isFetch, setIsFetch] = useState(false);
const loadMoreOrders = () => {
if (currentPage <= lastPage - 1) {
setLoading(true);
setCurrentPage((prevPage) => prevPage + 1);
console.log('loadMore??');
}
};
const getOpenOrders = useCallback(() => {
let AuthStr =
'Bearer ';
const headers = {
'Content-Type': 'application/json',
Authorization: AuthStr,
};
Api.post(
`/open_orders?page=${currentPage}`,
{},
{
headers,
},
)
.then((res) => {
let last_Page = res.data.open_orders.last_page;
let allOpenedOrders = res.data.open_orders.data;
console.log('allOpenedOrders', allOpenedOrders);
console.log('last_Page', last_Page);
console.log('currentPage', currentPage);
setLastPage(last_Page);
setOpenedAppointment((prevOpenedOrders) => [
...prevOpenedOrders,
...allOpenedOrders,
]);
setLoading(false);
setIsFetch(false);
})
.catch((err) => console.log('err', err));
}, [currentPage]);
// Delete Appointments
const cancelAppointem = (appointmentID) => {
let AuthStr =
'Bearer...';
const headers = {
'Content-Type': 'application/json',
Authorization: AuthStr,
};
Api.post(
'/cancel/order',
{
id: appointmentID,
},
{
headers,
},
)
.then((res) => {
setIsOpenedCancelModal((opened) => !opened);
console.log(res.data);
setOpenedAppointment((prev) =>
prev.filter((item) => item.id !== appointmentID),
); // works very well
})
.catch((err) => console.log(err));
};
const _refresh = () => {
setIsFetch(true);
getOpenOrders();
};
useEffect(() => {
getOpenOrders();
}, [getOpenOrders]);
const keyExtractor = (item, index) => String(index);
return (
<FlatList
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.flatListCon}
data={openedAppointment}
ListEmptyComponent={renderEmpty}
renderItem={renderItems}
keyExtractor={keyExtractor}
ListFooterComponent={_renderFooter}
onEndReached={loadMoreOrders}
onEndReachedThreshold={0.1}
onRefresh={_refresh}
refreshing={isFetch}
/>
);
};
Figure out the problem
At first render I got 5 orders from API, If I update the first order then pull to refresh, I got the same 5 order again but order number one is updated
So in getOpenOrders function, it merges old orders with new orders so I can see duplicated orders first 5 order "old data"
second 5 order "updated data".
But I have other concepts for load more "pagination" so it's should I have the merged arrays to get new order at the next page "2" with the prev order in the previous page "1"!
So how can I fix this?
Edit Data structure response
open_orders = [
{id: 14, status: "Cash", cost: "25.00", service_id: 11, vendor_id: 3, …}
{id: 15, status: "Cash", cost: "25.00", service_id: 11, vendor_id: 3, …}
...
]
Your effect should listen isFetch
const _refresh = () => {
setIsFetch(true);
};
useEffect(() => {
getOpenOrders();
}, [isFetch]);
const [updatePage, setUpdatePage] = useState(1)
const [isUpdate, setIsUpdate] = useState(false)
// call `setUpdatePage` when you edit the data
/**
* const edit = (record)=>{
* ...
* const updatePageIndex = openedAppointment.findIndex((item)=> item.id === record.id)
* const updatePageNum = Math.ceil(updatePageIndex/openedAppointment.length)
* setUpdatePage(updatePageNum)
* setIsUpdate(true)
* }
*
**/
const getOpenOrders = useCallback(() => {
const pageNum = isUpdate ? updatePage : currentPage
let AuthStr =
'Bearer ';
const headers = {
'Content-Type': 'application/json',
Authorization: AuthStr,
};
Api.post(
`/open_orders?page=${pageNum}`,
{},
{
headers,
},
)
.then((res) => {
let last_Page = res.data.open_orders.last_page;
let allOpenedOrders = res.data.open_orders.data;
...
if(openedAppointment.length > 0) {
const newOpenedOrders = openedAppointment.map((item)=>{
const target = allOpenedOrders.find(ele=> ele.id === item.id) || []
return {
...item,
...target,
}
})
setOpenedAppointment(newOpenedOrders);
} else {
setOpenedAppointment(allOpenedOrders)
}
setIsUpdate(false)
...
})
.catch((err) => console.log('err', err));
}, [currentPage]);
Related
how can I run a function in the whole app? like if that function is triggered then even if a user is navigating to a different screen then also that function will do its work until that function work is done how can I do this in react native? I want to do this with my PostImageHandler once postimagehandler is triggered then that function will run and it won't stop until the image is uploaded also user can navigate through different screens during that process
const [loading1, setLoading1] = useState(false)
const [loading2, setLoading2] = useState(false)
const [photo, setPhoto] = useState(null)
const { postImage } = route.params;
const PostImageHandler = useCallback(async () => {
if (!postImage.cancelled) {
const response = await fetch(postImage);
const blob = await response.blob();
const filename = postImage.substring(postImage.lastIndexOf('/') + 1);
const ref = firebase.storage().ref().child(filename);
const snapshot = await ref.put(blob);
const url = await snapshot.ref.getDownloadURL();
setPhoto(url)
console.log(url)
}
}, [postImage])
useEffect(() => {
PostImageHandler();
}, [PostImageHandler])
const handleUpload = useCallback(() => {
if (postImage != null) {
AsyncStorage.getItem('user')
.then(data => {
setLoading2(true)
fetch('https://mybackend.com/addpost', {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: JSON.parse(data).user.email,
post: postImage,
})
})
.then(res => res.json())
.then(data => {
if (data.message == 'Photo added successfully') {
alert('Photo added successfully')
setLoading2(false)
navigation.navigate('home')
} else {
alert('Something went wrong, please try again')
setLoading2(false)
}
})
})
} else {
alert('Please wait photo is processing')
}
}, [photo, navigation, setLoading2]);
I am trying to get the data from this endpoint but I am getting an empty array. Can you help me with this? thanks
I've been trying to do it, with the other methods I have I can get the corresponding data.
I tried several ways but still can't find a solution.
http://localhost:8081/getmnemonicsandtagsforabm this is the methood
const DataTable = () => {
const provider = useContext(AppContext);
let [editServiceStatus, setEditServiceStatus] = useState(false);
let [configServiceStatusButton, setConfigServiceStatusButton] =
useState(false);
let [serviceItem_selected, setServiceItem_selected] = useState([]);
let [fetchServiceDetail, setFetchServiceDetail] = useState([]);
let sessionID = localStorage.getItem("session_id");
const [array_services, setArray_services] = useState({ khad_services: [] });
const [array_services2, setArray_services2] = useState({ khad_services: [] });
const [active_dropdown, setActive_dropdown] = useState(false);
const [active_serviceConfig, setActive_serviceConfig] = useState(false);
useEffect(() => {
let session_id = {
session_id: sessionID,
};
fetch("http://localhost:8081/getservicelist", {
method: "POST",
body: JSON.stringify(session_id),
})
.then((res) => res.json())
.then((data) => {
//console.log(`****__ data: __****: `, data);
localStorage.setItem("array_services", JSON.stringify(data));
setArray_services(data);
})
.catch((err) => console.log(err));
}, []);
useEffect(() => {
let session_id = {
session_id: sessionID,
};
fetch("http://localhost:8081/getmnemonicsandtagsforabm", {
method: "POST",
body: JSON.stringify(session_id),
})
.then((res) => res.json())
.then((data) => {
console.log(`****__ data:__****: `, data);
localStorage.setItem("mnemonicsAndTags", JSON.stringify(data));
localStorage.setItem("mnemonics", JSON.stringify(data.mnemonics));
localStorage.setItem("tags", JSON.stringify(data.tags));
localStorage.setItem("tagsForMnemonics", JSON.stringify(data.tagsForMnemonics));
setArray_services(data);
})
.catch((err) => console.log(err));
}, []);
I await the return of the array accordingly
I'm getting a 400 client error and saying state.category.push is not a function and the current state is pending. I will be happy if someone helps me out on this. The category array is not accepting the data coming into it. I have both the form file and the reducer file down there. I am trying to create a category array to accepts users category and later output it out.
blogSlice.js
My Reducer file
const urlParams = new URLSearchParams(window.location.search)
const TokenAuthless = urlParams.get('enter')
if(TokenAuthless){localStorage.setItem('authless',
JSON.stringify(TokenAuthless))}
var Token = JSON.parse(localStorage.getItem("authless"))
const initialState = {
blogItems: [],
isLoading: null,
category: [{}],
authors: [],
}
const categoryUrl = 'api/v1/admin/createBlogCat';
var api_origin = 'xxxxxxxxxxxx'
export const insertCategory = createAsyncThunk('blog/insertCategory', async(data,
thunkAPI) => {
const{ rejectWithValue } = thunkAPI;
try {
const res = await fetch(`${api_origin}/${categoryUrl}`,{
method :'POST',
mode: 'cors',
body: JSON.stringify({data}),
headers : {
'Authorization': `Bearer ${Token}`,
'Content-type':'application/json',
'Accept':'application/json',
'Access-Control-Allow-Origin':'*',
},
})
const catData = await res.json();
return catData.data;
} catch (error) {
return rejectWithValue(error.message)
}
})
[insertCategory.pending]:(state, action) => {
state.isLoading = true;
},
[insertCategory.fulfilled]: (state, action) => {
state.isLoading = false;
console.log(action.payload);
console.log(current(state))
state.category.push(action.payload);
console.log('the state category', state.category);
},
[insertCategory.rejected]:( state, action ) => {
state.isLoading = false;
},
CreateCategory.js
Creating a form to accept the input here
const CreateCatAut = () => {
const [name, setName] = useState('');
const dispatch = useDispatch()
const handleSubmit=(e)=>{
e.preventDefault();
const categoryData = {name}
dispatch(insertCategory(categoryData));
console.log(categoryData)
}
return (
<form onSubmit={handleSubmit}>
<input type="text" placeholder="Active Input"
value={name}
onChange={(e)=> setName(e.target.value)} />
)
It means that your state.category is not an array. At
state.category.push(action.payload)
I assigned a value to the state.category which is not yet an array. By first creating an array and then putting that element into it.
state.category= [action.payload]
and at the next iterations, i will have an array with one element and can use push on it.
I have multiple API calls with fairly lengthy, yet similar, response/error handling for each call.
What is the best non-repetitive ways to make multiple independent api calls that update state using fetch?
Copying and pasting 40+ instances of fetch doesn't seem right.
I want to avoid doing this ....
fetch(url,options)
.then((response) => {
// ...
return response.json
})
.then((data) => {
setState(data)
//...
})
.catch((err) => {
//Error logic here
})
Here's what I've done so far:
I made (found and modified) a useFetch hook...
useFetch.ts
//Only calls fetch() when .load() is called.
const useFetch = (path : string, HttpMethod : string, dependencies : any = [] , body : {} | undefined = undefined) => {
const history = useHistory()
const [response, setResponse] = useState<any>({});
const [error, setError] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [controller, setController] = useState(2)
const [isReady, setIsReady] = useState<any>(false)
const load = ():void => {
setError("")
//This prevents useEffect from triggering on declaration.
if (isReady) {
//Math.random() is just to get useEffect to trigger.
setController(Math.random())
}
}
const token = localStorage.getItem("token");
let requestOptions:any = {
method: HttpMethod,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "* always",
Authorization: "Token " + token,
},
};
if (body !== undefined) {
requestOptions["body"] = {
body: JSON.stringify(body)
}
}
const URI = BASE_URI + path
useEffect(() => {
const fetchData = async () => {
if (controller !== 2) {
setIsLoading(true);
try {
const res = await fetch(URI, requestOptions);
const json = await res.json();
if (json?.action == "ENFORCE_BILLING" ) {
history.push(BILLING_CREDENTIALS_PATH, { enforceBillingPopUp: true });
}
if (json?.action == "ENFORCE_SMS_CONFIRMATION") {
// Should we log user out, as well?
history.push(CONFIRMATION_CODE_PATH)
}
if (res.ok) {
setResponse(json);
setIsLoading(false)
} else {
setError(json)
setIsLoading(false)
}
} catch (err) {
setError(err);
// Error logic here...
}
}
}
};
fetchData()
setIsReady(true)
}, [controller, ...dependencies]);
return { response, setResponse ,error, isLoading, load, isReady };
};
Component.tsx
//Inside react functional component...
// Prepares to fetch data from back-end
const data1 = useFetch(PATH1, "GET");
const data2 = useFetch(PATH2, "GET");
const data3 = useFetch(PATH3, "GET");
useEffect(() => {
// Initial on load data fetch
// .load() fetches data
data1.load();
data2.load();
data3.load();
}, [activeReservations.isReady]);
// Sort data depending on sort selection
...
Is useFetch considered bad practice? What are the advantages of using Redux, instead?
Any help would be greatly appreciated. Thanks.
I've got components in my react app,
First:
const App = () => {
const [tasks, setTasks] = useState([])
useEffect(() =>{
getTasks(data => setTasks(data))
}, [])
const onNewTask = task => {
setTasks(prev => ([
...prev,
task
]))
}
const onRemoveTask = () => {
setTasks(prev => ([
...prev,
]))
}
return (
<>
<NewTask add={onNewTask}/>
<Task tasks={tasks} remove={onRemoveTask}/>,
</>
)
}
This component renders two childrens:
In comp newTask there is a method where user can add a new task, the method:
const addNewTask = e => {
e.preventDefault();
fetch(`${API_URL}/tasks`, {
method: 'POST',
headers: {
Authorization: API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify(inputs)
})
.then(response => response.json())
.then(data => add(data.data))
.catch(e => console.warn(e))
}
It works fine, user adds a tasks, and it automatically appears in HTML thanks to onNewTask method in App component.
My problem is something similar, but in case of removing the task, I've got a method in Task component:
const handleDeleteTask = e => {
e.preventDefault();
const taskToRemove = e.target.id;
console.log(taskToRemove)
taskToRemove ?
fetch(`${API_URL}/tasks/${taskToRemove}`, {
method: 'DELETE',
headers: {
Authorization: API_KEY,
"Content-Type": "application/json",
},
})
.then(remove())
.catch(err => console.warn(err))
:
console.log('Error')
}
It removes a task, but user needs to refresh page to make the task disappear from HTML.
I know that my method isn't correct. Can someone please tell me how do it?
Thanks in advance.
you need to update the task array and set the same in setTasks.
const onRemoveTask = (id) => {
const newTaskList = tasks.filter(item.id != id)
setTasks(newTaskList)
}