UI Doesn't update after SWR data does NEXTJS - mongodb

I have a simple project going on and it is smoothly working but I have failed to add a item delete button. My post request to add items is perfectly working but my delete items doesn't work. I chose to go with post instead of delete because of my api structure.
Repo: https://github.com/berkaydagdeviren/rl-revenue-calculator
const handleClick = async (e) => {
e.preventDefault();
console.log(totalCredit, 'total credit before')
setTotalCredit([...totalCredit, credit])
console.log(ref, 'refFFFFFFF')
const currentDate = ref.current
const options = {
method: "PUT",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(
{ date: currentDate, credits: credit }
)
}
var responseClone;
fetch(`api/hello?date=${ref.current}`, options)
.then(res => {
responseClone = res.clone();
return res.json()
}).then(data => {
console.log(data, 'data')
}).then(function (data) {
// Do something with data
}, function (rejectionReason) { // 3
console.log('Error parsing JSON from response:', rejectionReason, responseClone); // 4
responseClone.text() // 5
.then(function (bodyText) {
console.log('Received the following instead of valid JSON:', bodyText); // 6
});
});
setCredit(0)
}
This is working perfectly fine but this does not;
const handleItemDelete = async itemToBeDeleted => {
console.log(itemToBeDeleted, "itemTobeDeleted")
const options = {
method: "PUT",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(
{ date: ref.current, index: itemToBeDeleted }
)
}
var responseClone;
await fetch(`api/hello?date=${ref.current}`, options)
.then(async res => {
responseClone = res.clone();
console.log(res, "res")
return await res.json()
// I used this to read server's response when it was giving parsing JSON error
}).then(data => {
console.log(data, 'data')
}).then(function (data) {
// Do something with data
}, function (rejectionReason) { // 3
console.log('Error parsing JSON from response:', rejectionReason, responseClone); // 4
responseClone.text() // 5
.then(function (bodyText) {
console.log('Received the following instead of valid JSON:', bodyText); // 6
});
});
const newTotalCredit = await data.find(item => item.date == ref.current).credits
setTotalCredit(newTotalCredit)
console.log("STATE UPDATED BEFORE DATA")
}
This is where I reference handleItemDelete to;
credit.map((item, index) => {
return (
item > 0 ?
React.Children.toArray(
<div>
<span style={{ color: 'green' }}> +{item}C </span>
<button onClick={() =>handleItemDelete(index)}>
X
</button>
</div>
)
:
null
)
})
}
And this is how I handle put request, again I can see that mongodb is updated after refresh but because ui didn't totalCredits' indexes are messed up and results in either no deletion or false deletion.
handler.put(async (req, res) => {
let data = req.body
console.log(typeof(data))
if (data.index) {
let {date, index} = req.body
console.log(data.index, "data.index")
await req.db.collection('credits').update({date: date}, {$unset: {["credits."+ index] : 1}})
await req.db.collection('credits').update({date: date}, {$pullAll: {credits: [null]}})
}
await req.db.collection('credits').updateOne({date: data.date}, {$push: {credits: data.credits}})
res.json(data)
})
I use SWR right in the index.js Home component
export default function Home()
{
const [totalCredit, setTotalCredit] = useState([])
const [credit, setCredit] = useState('')
const ref = useRef(null);
const [date, setDate] = useState(null);
const { data } = useSWR('/api/hello', async (url) => {const response = await axios.get(url);
return response.data; },
{ refreshInterval: 1000, revalidateOnMount: true });
Sorry if I'm not clear or providing wrong pieces of code please let me know. Thank you in advance!

your options in handleDeleteItem:
const options = {
method: "PUT",
headers: {
'Content-type': 'application/json'
},
Should not method be DELETE? You are sending PUT request instead of DELETE

Related

SWR optimistic mutation

I am building a small app to sign up users to sing a karaoke music. I am using SWR to fetch info from a mongoDB and due to performance issues I am trying to implement optimist UI. Although, I can't seem to make it work...
When a song is played/singed is marked as such with the code below:
const { mutate } = useSWRConfig();
const isSongPlayed = async (song: Song, bool: boolean) => {
try {
const isTheSongPlayed = await fetch(`/api/songs/${song?._id}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ ...song, played: bool }),
});
const playedSong = await isTheSongPlayed.json();
if (!playedSong.success || !playedSong.data) setLoading(true);
// the information to be changed is fetch is this endpoint
mutate(`/api/events/${id}`);
} catch (error) {
console.log(error);
}
};
// this function is called from a button
const onSetAsPlayed = (song: Song) => {
song.played ? isSongPlayed(song, false) : isSongPlayed(song, true);
};
Everything works pretty well locally, although the deployed version is very slow because it's counting on revalidation (call to the server) to update the UI. The code above isn't optimistic.
The flow is following:
singleEvent page:
displays a component to all requests;
displays a component to all moments:
Each moment display the songs list.
The object that is passed look like this:
{
eventTitle: '',
moments: [
{
momentTitle: '',
songs: [
{
title: '',
artist: '',
played: boolean,
requests: [user object]
},
],
},
],
};
What I have been doing is:
const isSongPlayed = async (song: Song, bool: boolean) => {
/* all code necessary to update the object above
which is basically marked the song as played and
pass it to the object again */
mutate(`/api/events/${id}`, newObjectUpdated, false);
try {
const isTheSongPlayed = await fetch(`/api/songs/${song?._id}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ ...song, played: bool }),
});
const playedSong = await isTheSongPlayed.json();
if (!playedSong.success || !playedSong.data) setLoading(true);
mutate(`/api/events/${id}`, newObjectUpdated, true);
} catch (error) {
console.log(error);
}
};
The code above works poorly, basically the updated song disappears and appears again with the changes which is a really bad UI.
Thanks in advance.
So I managed to fix it:
const isSongPlayed = async (song: Song, bool: boolean) => {
await fetch(`/api/songs/${song?._id}`, {
method: "PUT",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({ ...song, played: bool }),
});
const filterMoments = singleEvent.moments.filter((moment: any) => {
return moment._id !== momentData._id;
});
const filterSongs = momentData.songs.filter((s: any) => {
return s._id !== song._id;
});
const songUpdated = [...filterSongs, { ...song, played: bool }].sort(
(a, b) => {
if (a.createdAt < b.createdAt) {
return -1;
}
if (a.createdAt > b.createdAt) {
return 1;
}
return 0;
}
);
const momentsUpdated = [
...filterMoments,
{ ...momentData, songs: songUpdated },
].sort((a, b) => {
return a.index - b.index;
});
const eventUpdated = { ...singleEvent, moments: momentsUpdated };
mutate(`/api/events/${momentData.event}`, eventUpdated, false);
};
What happened is that I was revalidating the data and the event moments and songs wasn't sorted and originally, after a few time I figured out the problem. Not sure if it's the best solution, but seems serving the porpuse right.

upload and display images from cloudinary in nextjs + mongodb

(real estate nextjs application)
cannot send data with image URL to MongoDB and make a new property the API working Well,
and the MongoDB is connected i think the problem with the UI page i'm using formik for
form control , axios for API data fetching, cloudinary for images storing.
const [image, setImage] = useState([]);
const [property, setProperty] = useState(initialValues);
const { title, price, description, rentFrequency, rooms, baths, area, agency, purpose, furnishingStatus, amenities, city, garage, address, email, contact } = property;
console.log(property);
const handleOnChange = (e) => {
setProperty({ ...property, [e.target.name]: e.target.value });
};
const handleUploadInput = async (e) => {
const files = [...e.target.files];
const formData = new FormData();
for (let file of files) {
formData.append("file", file);
}
formData.append('upload_preset', 'my-uploads');
const res = await fetch(
"https://api.cloudinary.com/v1_1/shadow007/image/upload",
{
method: "POST",
body: formData,
}
);
const data = await res.json();
setImage([...image, data.secure_url]);
setProperty({ ...property, images: image }); // Maybe Im missing something around here
};
const handleOnSubmit = async (e) => {
e.preventDefault();
await createProperty();
};
// const validate = () => {
// if(!title || !price || !description || !content) return
// }
const createProperty = async () => {
try {
const res = await fetch("http://localhost:3000/api/properties", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(property),
});
const data = await res.json();
console.log(data);
} catch (err) {
console.log(err);
}

Nexjs + SWR: API resolved without sending a response for /api/projects/<slug>, this may result in stalled requests

Since on first render I was not able to get the router.query I am passing the params from getServerSideProps as follows:
export async function getServerSideProps(context) {
return {
props: { params: context.params },
};
}
Then in the function am trying to do the API call but am getting the API stalled error
API resolved without sending a response for
/api/projects/nichole_robel23, this may result in stalled requests.
This is my code:
export default function Project({ params }) {
const { slug } = params;
let [projectData, setProjectData] = useState([]);
let [loading, setLoading] = useState(true);
const { data } = useSWR('http://localhost:3000/api/projects/' + slug);
useEffect(() => {
if (data) {
setProjectData(data.data.project);
setLoading(false);
}
}, [data]);
......
I have global SWRCofig as follows
<SWRConfig value={{ fetcher: (url) => axios(url).then(r => r.data) }}>
<Layout>
<Component {...pageProps} />
</Layout>
</SWRConfig>
Any way to solve the problem?
You are missing your fetcher–the function that accepts the key of SWR and returns the data, so the API is not being called.
You are also not returning a response correctly from the API–this is most likely a case of not waiting for a promise/async to be fulfilled correctly.
CLIENT
const fetcher = (...args) => fetch(...args).then((res) => res.json());
export default function Home({ params }) {
const { slug } = params;
const [projectData, setProjectData] = useState([]);
const [loading, setLoading] = useState(true);
const { data } = useSWR(`http://localhost:3000/api/projects/${slug}`, fetcher);
useEffect(() => {
if (data) {
setProjectData(data);
setLoading(false);
}
}, [data]);
API
const getData = () => {
return new Promise((resolve, reject) => {
// simulate delay
setTimeout(() => {
return resolve([{ name: 'luke' }, { name: 'darth' }]);
}, 2000);
});
}
export default async (req, res) => {
// below will result in: API resolved without sending a response for /api/projects/vader, this may result in stalled requests
// getData()
// .then((data) => {
// res.status(200).json(data);
// });
// better
const data = await getData();
res.status(200).json(data);
}

react-admin authentication flow not working

Working on authentication react-admin using JWT token and storing in memory as closure variable.
Creating AuthToken from Django works fine, it's emitting the Token with user info as JSON.
Here is inMemoryJWT Manager
File: inMemoryJwt.js
// https://github.com/marmelab/ra-in-memory-jwt
const inMemoryJWTManager = () => {
let inMemoryJWT = null;
let isRefreshing = null;
// Token code goes here
const getToken = () => inMemoryJWT;
const setToken = (token) => {
inMemoryJWT = token;
return true;
};
const ereaseToken = () => {
inMemoryJWT = null;
return true;
}
return {
ereaseToken,
getToken,
setToken,
}
};
export default inMemoryJWTManager();
File: AuthProvider.js
oryJWTManager';
const authProvider = {
login: ({ username, password }) => {
const request = new Request('http://127.0.0.1:8000/api/token-auth/', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
return fetch(request)
.then(response => {
console.log('Response status ' + response.status )
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
console.log('response.json is ');
console.log(response.json());
return response.json();
})
.then(({ token }) => {
console.log(' about to set token in storage .......... ');
JwtManager.setToken(token);
const decodedToken = decodeJwt(token);
# Its not setting in localstorage
localStorage.setItem('token', token);
console.log('Token from lS : -------------',localStorage.getIdentity('token'));
localStorage.setItem('permissions', decodedToken.permissions);
})
.then(auth => {
localStorage.setItem('auth', JSON.stringify(auth));
})
.catch(response => {
console.log('Catch : ' );
console.log(response.json());
throw new Error('Network errorkkkkkkkkkkkkk')
});
},
logout: () => {
localStorage.setItem('not_authenticated', true);
localStorage.removeItem('auth');
localStorage.removeItem('role');
localStorage.removeItem('login');
localStorage.removeItem('user');
localStorage.removeItem('avatar');
inMemoryJWT.ereaseToken();
return Promise.resolve();
},
checkError: ({ status }) => {
if (status === 401 || status === 403) {
inMemoryJWT.ereaseToken();
return Promise.reject();
}
return Promise.resolve();
// return status === 401 || status === 403
// ? Promise.reject( { redirectTo: '/login' , logoutUser: false} )
// : Promise.resolve();
},
checkAuth: () => {
return inMemoryJWT.getToken() ? Promise.resolve() : Promise.reject({ redirectTo: '/no-access', message: 'login.required' });
// localStorage.getItem('auth')
// ? Promise.resolve()
// : Promise.reject({ redirectTo: '/no-access', message: 'login.required' }),
},
getPermissions: () => {
return inMemoryJWT.getToken() ? Promise.resolve() : Promise.reject();
// const role = localStorage.getItem('permissions');
// return role ? Promise.resolve(role) : Promise.reject();
},
getIdentity: () => {
try {
const { id, fullName, avatar } = JSON.parse(localStorage.getItem('auth'));
return Promise.resolve({ id, fullName, avatar });
} catch (error) {
return Promise.reject(error);
}
// return {
// id: localStorage.getItem('login'),
// fullName: localStorage.getItem('user'),
// avatar: localStorage.getItem('avatar'),
// };
},
}
export default authProvider;
App.js
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const { token } = JSON.parse(localStorage.getItem('auth'));
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
};
const dataProvider = drfProvider('http://127.0.0.1:8000/api/token-auth/', httpClient);
const App = () => (
<Admin title=""
loginPage={Login}
catchAll={NotFound}
logoutButton={PfsLogoutButton}
authProvider={authProvider}
dataProvider={dataProvider}
catchAll={NotFound}
dashboard={Dashboard} disableTelemetry>
The problem
The problem I am facing is to, It's not setting in-memory or local storage, also I get the message in the footer on the login button clicked. I am breaking my head with it for quite some time. Let me know if you need any other info.
Login required error for every page
HTTP Response goes here

Upload Image using Axios in React Native on Android

I'm trying to upload a file using axios and the request fails. have searched quite a bit seems like everyone is doing the same and works for them. Is there something I'm missing ?
My request in saga looks like this:
function* postUploadUtilityList(list) {
console.log('List', list.data);
const { data } = list;
for (const i in data) {
if (list.data.hasOwnProperty(i)) {
yield call(postUploadUtility, data[i]);
}
}
}
const createFormData = (photo, body) => {
const data = new FormData();
data.append('image', photo);
Object.keys(body).forEach((key) => {
data.append(key, body[key]);
});
return data;
};
function* postUploadUtility(item) {
console.log('item', item);
try {
const body = {
caption: 'utility',
};
const formData = createFormData(item, body);
const apiConfig = {
method: 'post',
baseURL: getBaseUrl(BB),
url: '/client/upload',
data: formData,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
};
const res = yield call(http, apiConfig);
if (res.status === 200) {
const { data } = res;
yield put({
type: POST_UTILITY_UPLOAD_SUCCESS,
data,
});
} else {
const { data } = res;
yield put({
type: POST_UTILITY_UPLOAD_FAILURE,
data,
});
}
} catch (e) {
yield put({
type: POST_UTILITY_UPLOAD_FAILURE,
e,
});
}
}
export default function* watchPostUploadUtility() {
yield takeLatest(POST_UTILITY_UPLOAD, postUploadUtilityList);
}
Log looks like as follow:
network call looks like as follow:
Try this solutions, Use image base64 instead of image url