I have a React form with Material-UI. I would like to get the id from the URL link using useParams and make some API requests in order to populate form-data:
http://localhost:3001/profile?ids=9459377
Main app.tsx:
function App() {
return (
<Router>
<Navbar />
<Switch>
<Route path='/ticket-profile/:ids' component={TicketProfile} />
</Switch>
</Router>
);
}
I use this code to open a new page and pass ids param:
history.push(`/ticket-profile/ids=${id}`)
I need to get the data into this page:
export default function TicketProfile(props: any) {
let { ids } = useParams();
const [ticket, setTicket] = useState<TicketDTO[]>([]);
useEffect(() => {
getSingleTicket();
}, []);
const getSingleTicket = async () => {
getTicket(ids)
.then((resp) => {
setTicket(resp.data);
})
.catch((error) => {
console.error(error);
});
}
But for this line let { ids } I get:
TS2339: Property 'ids' does not exist on type '{}'.
Do you know how I can fix this issue?
So this is the url
http://localhost:3001/profile?ids=9459377
In your code
const MyComponent = () => {
const params = new URLSearchParams(window.location.search);
That's it! Now we should move on to getting the value and checking the existence of the query strings
Check if it has the query;
params.has('ids')
or get the value that is inside the query string
params.get('ids')
You can also show them conditionally
console.log(params.has('ids')?params.get('ids'):"")
Update:
Check out the working example
https://codesandbox.io/s/peaceful-https-vz9y3?file=/src/App.js\
This is how we should use it in your case
export default function TicketProfile(props: any) {
const params = new URLSearchParams(window.location.search);
const ids = params.get('ids');
const [ticket, setTicket] = useState<TicketDTO[]>([]);
useEffect(() => {
getSingleTicket();
}, []);
const getSingleTicket = async () => {
getTicket(ids)
.then((resp) => {
setTicket(resp.data);
})
.catch((error) => {
console.error(error);
});
}
I guess you are using useParams from react-router. If so then try so here
let { ids } = useParams<{ ids: Array<any>}>();
You are pushing a wrong path to the history. Try it like this:
history.push(`/ticket-profile/${id}`)
Related
I am trying to render listed property information from an array of objects. I used this method in another part of my project with success, but in this instance, I am not getting anything at all.
here is the code I have
import { database } from "../../components/firebase";
import { ref, child, get } from "firebase/database";
import { useState, useEffect } from "react";
export default function Dashboard() {
const dbRef = ref(database);
const [users, setUsers] = useState([]);
const array = [];
const getData = () => {
get(child(dbRef, "users/"))
.then((snapshot) => {
const data = snapshot.val();
setUsers(data);
})
.catch((err) => {
console.log(err);
});
};
const getProperties = () => {
Object.values(users).forEach((user) => {
Object.values(user?.properties).forEach((property) => {
array.push(property);
console.log(property);
});
});
console.log(array);
};
useEffect(() => {
getData();
getProperties();
}, [dbRef]);
return (
<>
<div>Properties </div>
<div>
{array.map((property) => (
<div key={property.property_id}>
<h1>{property?.property_name}</h1>
<p>{property?.description}</p>
<p>{property?.rooms}</p>
<p>{property?.phone}</p>
</div>
))}
</div>
<p>oi</p>
</>
);
}
Nothing happens, it only prints "properties" and "oi"
getData is asynchronous. When you execute getProperties, your users state will still be its initial, empty array value.
You don't appear to be using users for anything else but assuming you want to keep it, the easiest way to drive some piece of state (array) from another (users) is to use a memo hook.
// this is all better defined outside your component
const usersRef = ref(database, "users");
const getUsers = async () => (await get(usersRef)).val();
export default function Dashboard() {
const [users, setUsers] = useState({}); // initialise with the correct type
// Compute all `properties` based on `users`
const allProperties = useMemo(
() =>
Object.values(users).flatMap(({ properties }) =>
Object.values(properties)
),
[users]
);
// Load user data on component mount
useEffect(() => {
getUsers().then(setUsers);
}, []);
return (
<>
<div>Properties </div>
<div>
{allProperties.map((property) => (
<div key={property.property_id}>
<h1>{property.property_name}</h1>
<p>{property.description}</p>
<p>{property.rooms}</p>
<p>{property.phone}</p>
</div>
))}
</div>
<p>oi</p>
</>
);
}
The memo hook will recompute allProperties any time users is changed.
If you don't need the users state, then there's not much need for the memo hook. Instead, just maintain the state you do need
const [allProperties, setAllProperties] = useState([]); // init with empty array
useEffect(() => {
getUsers().then((users) => {
setAllProperties(
Object.values(users).flatMap(({ properties }) =>
Object.values(properties)
)
);
});
}, []);
In react i need to be able to open a popup window https://developer.mozilla.org/en-US/docs/Web/API/Window/open and manage the events such as "mesage" https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage and "load" and "close" events.
However none of the events i have added listeners to are firing...
import * as React from 'react';
import './style.css';
import { useState, useRef } from 'react';
export default function App() {
const { login, error } = useOAuth();
return (
<div>
<button onClick={login}>Login</button>
</div>
);
}
const useOAuth = () => {
const [error, setError] = useState();
const popupRef = useRef<Window | null | undefined>();
const login = () => {
popupRef.current = openPopup('https://google.com');
popupRef.current.addEventListener('load', handlePopupLoad);
popupRef.current.addEventListener('close', handlePopupClose);
popupRef.current.addEventListener('message', handlePopupMessage);
};
const handlePopupLoad = (data) => {
console.log('load', data);
};
const handlePopupClose = (data) => {
console.log('close', data);
};
const handlePopupMessage = (data) => {
console.log('message', data);
};
const openPopup = (url: string) => {
const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=500,height=600,left=100,top=100`;
return window.open(url, 'Login', params);
};
return {
login,
error,
};
};
https://stackblitz.com/edit/react-ts-qlfw9q?file=App.tsx
aside:
Is there a way to differentiate between when a "user" closed the window using the "red x" button and when it was correctly closed using window.close().
how can i nicely cleanup the popup once its closed.
I have changed the URL to a local one (to avoid any cross-origin issues).
Check out the demo (If this fails to load, try refreshing. Something seems to be off with Stackblitz)
In parent page, I have used the onload (for loading), onunload (for close)
import * as React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/auth" element={<AuthPage />} />
</Routes>
</BrowserRouter>
);
}
function Home() {
const login = () => {
console.clear();
const url = '/auth';
const popup = openPopup(url);
// When the popup loads
popup.onload = () => {
console.log('loaded. this was logged');
};
// when the popup unloads
popup.onunload = () => {
console.log('unloading now');
};
// when the popup posts a message
popup.addEventListener('message', ({ data }) => {
console.log('message: ', data);
});
};
const openPopup = (url: string) => {
const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=500,height=600,left=100,top=100`;
return window.open(url, 'Login', params);
};
return (
<div>
<h1>Home</h1>
<button onClick={login}>Login</button>
</div>
);
}
function AuthPage() {
// I have added a button to trigger postMessage to parent.
const onClick = () => {
window.parent.postMessage('To parent');
};
return (
<div>
<h1>Auth Page</h1>
<button onClick={onClick}>Click me</button>
</div>
);
}
Few things I observed:
Since we are posting message from child to parent, I would expect window.addEventListener('message') to get triggered. But, for some reason, is popupRef.current.addEventListener('message') getting triggered.
popupRef.current.onunload gets triggered before the beginning of onload. If I had to guess, this is some sort of cleanup mechanism.
I think there is no react specific issue all you need to do to make this code work is to change your login function something like this.
const login = () => {
const childWindow = openPopup('https://same-origin.com');
childWindow.addEventListener('load', handlePopupLoad);
childWindow.addEventListener('close', handlePopupClose);
childWindow.addEventListener('message', handlePopupMessage);
};
well, you should probably wrap all your event listeners inside a useEffect to run it and cleanup after it, it should look like something like this
const popupRef = useRef<Window | null>(null)
const handlePopupLoad = (data: any) => {
console.log('load', data)
}
const handlePopupClose = (data: any) => {
console.log('close', data)
}
const handlePopupMessage = (data: any) => {
console.log('message', data)
}
const openPopup = (url: string) => {
const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=500,height=600,left=100,top=100`
return window.open(url, 'Login', params)
}
useEffect(() => {
if (!popupRef.current) {
return undefined
}
popupRef.current = openPopup('https://google.com')
popupRef.current?.addEventListener('load', handlePopupLoad)
popupRef.current?.addEventListener('close', handlePopupClose)
popupRef.current?.addEventListener('message', handlePopupMessage)
return () => {
popupRef.current?.removeEventListener('load', handlePopupLoad)
popupRef.current?.removeEventListener('close', handlePopupClose)
popupRef.current?.removeEventListener('message', handlePopupMessage)
}
}, [popupRef])
I am having trouble logging in to my application using the Context API. When I run applications without having any token in my localStorage in the variable session I get a lot of errors like below:
Uncaught TypeError: Cannot read properties of null (reading 'name')
I think that this problem exists because my currentAccount from ApplicationContext is null.
dashboard/index.tsx
const { currentAccount } = useContext(ApplicationContext);
return (
<span>{currentAccount.name}</span>
);
On the routes.login login page I am also getting these exceptions even though this error should only be on routes.dashboard :/ Refreshing the page or clearing localStorage does not help. I;m having also an infinite loop over checkLogin in ApplicationContextProvider :(
login/index.tsx
const { setCurrentAccount } = useContext(ApplicationContext);
const onFinish = async (email: string; password: string) => {
try {
const response = await axios.post("/auth/login", {
email: email,
password: password
});
const token = response["token"];
const account = response["account"];
if (token && account) {
localStorage.setItem("session", token);
setCurrentAccount(account);
history.push(routes.dashboard)
}
} catch (error) {
}
};
App.tsx
return (
<div className="App">
<Switch>
<ApplicationContextProvider>
<Route path={route.login} component={Login} />
<Main>
<Route path={route.dashboard} component={Dashboard} />
</Main>
</ApplicationContextProvider>
</Switch>
</div>
);
ApplicationContextProvider.tsx
export type AccountContext = {
currentAccount?: Account;
setCurrentAccount: (user: Account) => void;
checkLogin: () => void;
};
export const ApplicationContext = React.createContext<AccountContext>(null);
interface ProviderProps {
children: React.ReactNode
}
export const ApplicationContextProvider = ({ children }: ProviderProps) => {
const [currentAccount, setCurrentAccount] = useState<Account>(null);
useEffect(() => {
checkLogin();
}, [currentAccount]);
const checkLogin = async () => {
const token = localStorage.getItem("session");
if (token) {
const token = localStorage.getItem("session");
const decode = jwt(token);
const query = {
id: decode["id"]
}
const response: Account = await api.get("/auth/account", query);
setCurrentAccount(response);
} else {
setCurrentAccount(null);
}
};
const stateValues = {
currentAccount,
setCurrentAccount,
checkLogin
};
return (
<ApplicationContext.Provider value={stateValues}>
{children}
</ApplicationContext.Provider>
);
Can someone tell me what is wrong with my context logic to authentication user to application?
Thanks for any help!
I already know what's going on, maybe it will be useful to someone in the future.
I have to add at two important things. For the first useEffect() function need to have an empty dependency array like below:
useEffect(() => {
checkLogin();
}, [ ]);
and for second I have to add also state to store loading like below:
const [loading, setLoading] = useState(false)
and set correct values when i've got data from api and so on
Currently working on a stock project for my portfolio and I am using finnhub as the API.
I can log everything to my console. However I cannot render it as the "data" is not globally declared and must be inside of a certain function.
I tried rendering globally but had no luck...
So my question is how do I make 'data' global so that I can render it inside of the "StockHeader's" return ?
Heres what I have so far...
import React,{ useState, useEffect } from 'react';
const StockHeader = (data) => {
const [stocks, setStocks] = useState({});
const getStocks = () => {
//setting stocks
setStocks(stocks)
}
//calling it once
useEffect(()=> {
getStocks();
}, [])
//using finhubs ready made code from documentation
const finnhub = require('finnhub');
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "my apikey"
const finnhubClient = new finnhub.DefaultApi()
finnhubClient.quote("AAPL", (error, data, response) => {
//I can log the data but I cant show it in my component
console.log(data.c)
});
return (
<>
{/* This says that data is not defined */}
<h1>{data.c}</h1>
</>
)
}
export default StockHeader
You just need a little bit of code reorganization so that the API request only happens once and so that you can use setStocks to store it:
const StockHeader = (data) => {
const [stocks, setStocks] = useState({});
useEffect(()=> {
//this could be separated into a `getStocks` function if you want
const finnhub = require('finnhub');
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "my apikey"
const finnhubClient = new finnhub.DefaultApi()
finnhubClient.quote("AAPL", (error, data, response) => {
console.log(data.c);
setStocks(data.c);
});
}, []);
return (
<>
{/* You probably don't want to render `stocks` itself, but this shows you how to get access to the variable */}
<h1>{stocks}</h1>
</>
)
}
First, i want to say that i'm beginner in react (and i hate front development but, you know, sometimes we don't choose in the job's life)....
So, i create a custom form with react-admin without use the REST connexion from react-admin (it's a specific form).
After the form's validation, a value named processingStatut of several data change and need to show this new value in the
<List><Datagrid> mapped by react-admin.
So i follow the documentation for create a reducer action for change a boolean value named processingStatut in my dataGrid like this:
epassesReceived.js
export const EPASSES_RECEIVED = 'EPASSES_RECEIVED';
export const epassesReceived = (data) => ({
type: EPASSES_RECEIVED,
payload: { data },
});
my customForm.js
import { epassesReceived as epassesReceivedAction } from './epassesReceived';
handleSubmit(event) {
this.setState({
post: this.post
});
const { fetchJson } = fetchUtils;
const {
showNotification,
history,
push,
epassesReceived,
fetchStart, fetchEnd
} = this.props;
const url = `${API_URL}/ePasses/update`;
const datas = JSON.stringify(this.state);
const options = {
method: 'POST',
body: datas
};
fetchStart();
fetchJson(url, options)
.then( response => epassesReceived(response.json) )
.then(() => {
showNotification('ra.notification.epasseRecorded');
history.goBack();
})
.catch( error => {
console.error(error);
var message = error.message.replace(/ /g, '');
showNotification(`ra.notification.${message}`, 'warning');
})
.finally(fetchEnd);
event.preventDefault();
}
...
const mapStateToProps = state => ({
customReducer: state.customReducer
});
export const EpassesUpdate = connect(mapStateToProps, {
epassesReceived: epassesReceivedAction,
showNotification,
push,fetchStart, fetchEnd
})(translate(withStyles(formStyle)(EpassesUpdateView)));
and in my app.js
import { EPASSES_RECEIVED } from './epassesReceived';
const customReducer = (previousState = 0, { type, payload }) => {
console.log(payload, type);
if (type == EPASSES_RECEIVED) {
// console.log('modif');
// payload.data[0].processingStatut=1; this is the purpose of the script. To show de modification changed after form's validation
return payload;
}
return previousState;
}
and the viewDataGrid.js
<List
classes={props.classes}
{...props}
exporter={exporter}
title='ePass.pageTitle'
perPage={15}
pagination={<PostPagination />}
filters={<EPassFilter businessunit={businessUnit} />}
bulkActions={<EPassBulkActions businessunit={businessUnit} />}
actions={<PostActions businessUnit={businessUnit} />}
>
<Datagrid classes={props.classes}>
{ businessUnit === undefined || !businessUnit.companyName &&
<TextField source="businessUnitName" label="ePass.businessUnitName" />
}
<StateField source="processingStatut" label="" translate={props.translate} />
.....
But in my console log my value doesn't change and i don't now why... Of course it's works if i refresh my web page by F5 because the value is changed in my database. But not in react's dataGrid... I'm lost...
maybe the log output can be helpfull:
We can see the type "EPASSES_RECEIVED" and the data changed
i think your problem comes from your fetch. Try this :
fetch(url, options)
.then( response => response.json() )
.then(data => {
epassesReceived(data);
showNotification('ra.notification.epasseRecorded');
history.goBack();
})
.catch( error => {
console.error(error);
var message = error.message.replace(/ /g, '');
showNotification(`ra.notification.${message}`, 'warning');
})
.finally(fetchEnd);