Reflect changes without page refresh - javascript

I have created method to delete image as
action.js
export const removeImage = (id, image_id) => async () => {
try {
const response = await axios.delete(
`localhost:3000//api/v1/posts/${id}/delete/${image_id}`,
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
)
I have call this method in post/[id].js as
<p onClick={() => removeImage(id, imgae.id)}>delete </p>
The problem is when I delete image I should hard refresh page to see changes. How can I solve this.
I have used useEffect hook
useEffect(()=> {
fetchData();
}, [data]);
It worked but load backend server too much.
Another idea I think to use useState hook. but how can I implement in this code?
Thanks in advance

I think that you try to do two different things :
Delete the image on your server with a request
Hide the image from your component / refresh your component src
You need to make the source image property of your component more reactive, like putting it in the state so you can modify it dynamically
Because here, you simply delete the resource on your server but your component is not refreshed or reloaded.
What you shoud do is to hide your component holding the image and display a kind of placeholder after clicking your delete button

Unless your app watches the server for updates you should manually reflect the changes you do on the server in your UI as well. For example, if you have a component, say an ImageButton, that is showing your image, and you remove the image from the server, what is the intended behavior? If you want the image button removed, you can simply set it’s visible property to false.

Related

Navigate with page reload using react router

My application uses a Laravel backend and a React frontend. Ordinarily, I would use e.preventDefault() so that there are no page reloads. The issue I am having is that if I login or logout without a page refresh, it causes a CSRF mismatch on future requests. So I'm okay with the page refresh. However, when I remove e.preventDefault(), the form data becomes appended to the URL. The best solution I have been able to come up with is this:
let csrfToken = document.head.querySelector('meta[name="csrf-token"]').content;
const navigate = useNavigate()
const handleLogin = async (e) => {
e.preventDefault()
axios.post('login', {email, password}, {
'X-CSRF-TOKEN': csrfToken
})
.then(() => {
setLoginStatus(true)
navigate('/')
window.location.reload();
})
}
This essentially works, but I don't like it. What happens is that the view is updated with the Component at '/,' and then the page refreshes. It just looks weird. All I want is for the Component at '/' to be rendered upon the form's submission with a page reload. Keeping in mind my problem with removing e.preventDefault(), where the form data gets added to the URL.
I couldn't realize this problem. But I think you can use useEffect and useState together.
For example: When the login operation finishes, set the data which will be gained to state. And make dependency with useEffect and that state. So if the state change the component automaticaly re-render beacuse of the useEffect. In this case no reload needed.

Router.push or Link not rendering/refreshing the page even thought the url is updated nextjs

I apologize for my horrendous way of explaining my issue. I have shared a link below description which is exactly the same issue I am experiencing. Any kind of help would be greatly appreciated.
I have directory path like pages/request/[reqid].js . When my url is localhost:3000/xyz and I navigate to pages/request/1 by clicking a button on the current page, the page successfully loads the page with proper data from [reqid=1] but when I try to access pages/request/[reqid].js with different reqid (say suppose reqid=2), the url reflects the correct the reqid pages/request/2 but the page remains the same, doesn't change. However if I go back to other pages like localhost:3000/xyz and click a button there to navigate to pages/request/2 it works but from within pages/request/[reqid] it doesn't render a page associated to the corresponding reqid even thought the url is updated. I have tried both Link and router.push ,both fails to render the correct reqid page.
https://github.com/vercel/next.js/issues/26270
It actually failed to include that I was using getServerProps to fetch the data, which was the reason the page wasn't rendering , unless the page was manually refreshed. The page state is not reset for navigation between dynamic routes that served by the same source component.
for example, give page source /a/[param]/index.js, when navigating from /test/123 to /test/124, states on the page wasn't being reset.
So actually happened is the same React Component been rendered with different props. Thus react takes it as a component is rerendering itself, and causing the new navigated page receive stale states.
To fix it, just add {key: } to page initial props or getserversideprops
export const getServerSideProps = async (ctx) => {
try {
const { reqid } = ctx.params;
//fetch code
return {
props: {
key: reqid,
data:data
},
};
} catch (error) {
console.log(error);
}
};

How to use useEffect deps when page reload?

I got simple blog with arficles, and when user click edit button he got form filled with articles data - title, description, body and tags. I use useEffect to get data and fill form, when I got "id". If there is no "id" form should be blank. here is my useEffect:
useEffect(() => {
if (id) {
isLoading = true;
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}, [id]
);
but when I reload page id not changed, and func userService.articles.get(id) not run, and all datas gone. I need advice how to fix it? may be I need to use other deps for useEffect, but now I have no idea what deps i can use exept id.
upd:
thank you all for help. all i want is:
when the edit page load/reload and "id" exist -> fills form fields
when "id" not exist -> blank form fields
now when I reload edit page i got id - but all datas gone, and i got blank form :(
Here is the full code: codesandbox
p.s. i use free API - so you can create user in a sec with any imagined email, username and password. you don't need mail confirmation.
You should use this.props.match.params to access your id that comes from the url.
useEffect(() => {
if (props.match.params.id) {
setIsloading(true);
userService.articles.get(props.match.params.id)
.then((resp) => {
setIsloading(false);
props.onLoad(resp)
})
} else {
props.onLoad(null);
}
}, [props.match.params.id]);
Also you should rely on useState to manage your isLoading variable.
const [isLoading, setIsloading] = useState(false);
I did a bit more digging into the code you have provided.
The initialValues will be first empty because the data coming from the props is not there yet. And once the initialValues have been set you can't change them dynamically, you have to resort to the antd Form api.
You cannot set value for each form control via value or defaultValue
prop, you should set default value with initialValues of Form. Note that initialValues cannot be updated by setState dynamically, you
should use setFieldsValue in that situation.
The key here is to use another useEffect with dependencies to your form values comming from the props and use those to reset the form values via setFieldsValue.
try to useEffect without options and it will run just when the page loads for the first time
useEffect(() => {
if (id) {
isLoading = true;
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}, []
);
Based on the assumption that you want props.onLoad to run whenever there is a defined "id" or the defined "id" changes:
Returning a function from a useEffect hook (as you do with return props.onLoad(...)) is specifically for "cleaning up" things like side effects or subscriptions. A function returned inside a useEffect hook will only run when the component unmounts. See docs here. Also it doesn't seem like you are even passing a function to run on cleanup. You're passing the result of props.onLoad to run on cleanup, which based on the function name doesn't seem like it is intended to return another function.
So, if you want props.onLoad() to run if the "id" is defined, remove the return before props.onLoad. That return is telling React to hold (what it thinks is a function) for cleanup on unmount. If it's still not working, I think we'll need more information on what exactly props.onLoad is doing.

Vue: route navigation with router.resolve (to open a page in new tab) with data in params is lost

I have a component, lets call component 1. A method in component1 makes an axios.post request and the server returns a bunch of data. When data is loaded, a new button appears. When this button is clicked, it will be navigated to another route with another component, let call this component2. Now some of the loaded data from component1 needs to transferred to component2 and should be opened in new tab. Below is the code:
<script>
import axios from 'axios';
export default {
name: "CheckStandard",
data() {
return {
standard: '',
time: {},
programs: {},
example: {},
}
},
methods: {
checkData(){
let std= {
std: this.standard,
}
axios.post('http://localhost:3000/postdata', std)
.then(res => {
if (res.status === 200) {
if (res.data === 0) {
this.invalidID = "This Standard does not exist"
}
else {
let data = res.data
this.time = res.data["Starttime"];
this.programs = res.data["program"]
this.example = res.data["example"]
}
}).catch(error => {
console.log(error);
this.error = error.response
})
},
}
goToPictures(){
let route = this.$router.resolve({
name:'ProgramCheckList',
params: {
programs: this.programs,
time: this.time,
example: this.example
}
})
window.open(route.href,'_blank')
},
}
}
</script>
The function goToPictures is the function that is invoked after clicking the button. Now in this function goToPictures I have defined the route to navigate to another tab. But the problem the data in the params which it should carry is lost. I tried with $router.push ofcourse it works but it is not to open in new tab. Below is the code for the same:
goToPictures(){
this.$router.resolve({
name:'ProgramCheckList',
params: {
programs: this.programs,
time: this.time,
example: this.example
}
})
},
}
Since I am new to vue, I have tried my best to look for an answer for this, even I have came across some posts in several forums mentioning, it is may be not be possible even, instead advised to use vuex. But I still wanted to post it, maybe we have a solution now or any other idea. Thanks
The problem you're seeing stems from the fact that, when you open a new window, Vue is basically going to re-render your components as if you hit refresh. Your Component 2 has props that it can only inherit from another component, and as such, it has no possible way of knowing what the props it needs to use are.
To illustrate in simple terms what's happening:
The user navigates to Component 1. They click the button, which makes the GET request. You now have some data that you can pass onto Component 2 as props.
In a regular environment, the user would simply click on the link leading to Component 2, and the props would be passed on normally, and everything would work as intended.
The problem in your situation is that Component 2 depends on Component 1 for its data. By navigating directly to the Component 2 route (in this situation, opening a new window is functionally identical to a user copy/pasting the url into the adress bar), Vue never has the chance of interacting with Component 1, and never gets told where to get the props it needs to populate Component 2.
Overcoming the issue
There's a few things you can do here to overcome this issue. The most obvious one is to simply open Component 2 as you would normally, without opening a new window, but keep in mind that even if you do this, should a user copy/paste the URL where Component 2 is, they'll run into the exact same issue.
To properly deal with the issue, you have to specify a way for Component 2 to grab the data it needs. Since the data is already fetched, it makes sense to do this in the created() or mounted() hooks, though if you wanted to you could also deal with this in Vue Router's beforeRouteEnter() hook.
Vuex
While you don't necessarily need a state management tool like Vuex, it's probably the simplest way for your needs. When you grab the data from Component 1, store it and access it from the Component 2 mounted() hook. Easy-peasy.
localStorage()
Alternatively, depending on how the data is being served, you could use localStorage(). Since you're opening a new window, sessionStorage() won't work. Do note that localStorage() can only hold strings and nothing else, and isn't necessarily available in every browser.
You can store the data to a global variable and use that in the newly opened window. Asked here Can I pass a JavaScript variable to another browser window?
Provided the windows are from the same security domain, and you have a reference to the other window, yes.
Javascript's open() method returns a reference to the window created (or existing window if it reuses an existing one). Each window created in such a way gets a property applied to it "window.opener" pointing to the window which created it.
Either can then use the DOM (security depending) to access properties of the other one, or its documents,frames etc.
Another example from same thread:
var thisIsAnObject = {foo:'bar'};
var w = window.open("http://example.com");
w.myVariable = thisIsAnObject;
//or this from the new window
var myVariable = window.opener.thisIsAnObject;

Want to have an event handler for the browser's back button with next.js

I am having a modal which while opening pushes a hash to url example.com/#modal, on browser back button click, I want to recognise that event so that I can toggle the state of modal. the point is, since Im using it with next.js (server side rendering), I will not be having access to window object (correct me if I am wrong). so I need an alternate way to handle the event of browser back button.
You can use next/router's beforePopState to act on changes to the session history navigation (back/forward actions), and make sure it'll only happen when leaving the current page.
useEffect(() => {
router.beforePopState(({ as }) => {
if (as !== router.asPath) {
// Will run when leaving the current page; on back/forward actions
// Add your logic here, like toggling the modal state
}
return true;
});
return () => {
router.beforePopState(() => true);
};
}, [router]); // Add any state variables to dependencies array if needed.
#Summy I hope your issue is resolved by now. If not you can try this for the browser back button:-
Next.js + React Go back to the previous page
If you want to use hashbang URLs you can't use SSR, since /# and /#/one is the same route server-side, so there is no way for the server to know what to render, it will need to send a basic template and let the client fill it, in that case, I think using CRA or Parcel with React Router and its HashRouter is a better option, that way you will have a single index.html and let the client decide what to render.
in NextJs we can use beforePopState function and do what we want such close modal or show a modal or check the back address and decide what to do
https://stackoverflow.com/a/60702584/4717739
https://stackoverflow.com/a/69560739/4717739

Categories