Call Java methods in Javascript using React - javascript

I am trying to make a webpage that displays the price of assets from different exchanges. I have a java class that makes the http requests and now I need to somehow call those variables with my js code that is designing my webpage. Any help would be amazing, and please let me know if there is anything else that should be added code-wise to help determine my issue?
I figure the calls go around here, but I am unsure if I need to also do anything in my java class, like save the variables in certain formats as right now they are in maps.
<div className = 'Middle'>
<Exchange name = "Coinbase" btcBuy = "" btcSell = "" ethBuy = "" ethSell = ""/>
<Exchange name = "Binance" btcBuy = "" btcSell = "" ethBuy = "" ethSell = ""/>
<Recommendations/>
</div>

There are a couple of issues. The first is how you deal with state - once you've got your data how can your component render it. The second is how do you call an endpoint lots of times, but still update the state with just one collection of data.
First: React functional components can use hooks, and in this example we're using both useState to store the data, and useEffect to get the data.
Second: build an array of promises (where each element in the array is a new fetch to the server), and then use Promise.all to wait for all of the server calls to resolve. You can then update the state with the data, and then use that state to render the component, and its child components.
The code will look vaguely like this.
const { useEffect, useState } = React;
function Example() {
const [ data, setData ] = useState([]);
useEffect(() => {
// Generic fetch params
const arr = [1, 2, 3];
async getData() {
// Get an array of fetch promises using the params
// Wait for them to resolve, return the parsed data, and
// then set the state with that data
const promises = arr.map(el => fetch(`endpoint?query${el}`));
const response = await Promise.all(promises);
const data = response.map(async el => await el.json());
setData(data);
}
getData();
}, []);
// `map` over the data to create your
// Exchange components
return (
<div>
{data.map(obj => {
<Exchange
name={obj.name}
btcBuy=""
btcSell=""
ethBuy=""
ethSell=""
/>
})}
</div>
);
};
ReactDOM.render(
<Example />,
document.getElementById('react')
);

Related

Local storage with hooks in react

So I have an array with the following structure:
`
export const transacciones = [
{
id:100,
cantidad: 0,
concepto : 'Ejemplo',
descripcion: 'Ejemplo',
},
]
`
This array will dynamically increase or decrease as I push or filter items in it (Exactly like data in a task list)
The problem is that I am trying to add some data persistence using local storage. I guess data is getting stored but not shown when I refresh my browser (chrome).
However, when I refresh data disappears from where it was in the upper image so I`m not even sure if I am correctly storing it.
I've tried two things using useEffect hooks.
First aproach:
`
const [transacciones,setTransacciones] = useState([]);
useEffect(()=>{
localStorage.setItem('transacciones',JSON.stringify(transacciones))
},[transacciones])
useEffect(() =>{
const transacciones = JSON.parse(localStorage.getItem('transacciones'))
if (transacciones){
setTransacciones(transacciones)
}
},[])
`
I read somewhere that as the initial value of use state is [] I should chage things in there, so...
Second aproach:
`
const [transacciones,setTransacciones] = useState([],()=>{
const localData = localStorage.getItem('transacciones');
return localData ? JSON.parse(localData) : [];
});
useEffect(()=>{
localStorage.setItem('transacciones',JSON.stringify(transacciones))
},[transacciones])
`
However, when I refresh I get the same result: No persistence.
What am I missing here? Any help would be appreciated
In both scenarios your transacciones array is empty when you perform the localStorage.setItem. if you're trying to keep your local state sync with localStorage this might help:
export function useTransacciones(initialValue){
const localData = localStorage.getItem('transacciones');
const [transacciones,_setTransacciones] = useState(localData?JSON.parse(localData) : initialValue); // you can choose your own strategy to handle `initialValue` and cachedValue
const setTransacciones = (data) => {
_setTransacciones(data)
localStorage.setItem(JSON.stringify(data))
}
hydrate(){
const data = localStorage.getItem("transacciones")
setTransacciones(JSON.prase(data))
}
return [ transacciones, setTransacciones, hydrate ]
}
which you can use it anywhere with caching compelexity hidden inside:
const [transacciones, setTransacciones] = useTransacciones([])

How to persist data from useLocation()?

is there a way to persist data from useLocation() ?
Basically I'm sending data that is located on a nested collection from firebase
(There's a lot of background on my question/explanation, you can just skip to the important bit)
Usually you have no issues when you only have one collection you can always access the UID pretty much from anywhere by sending it from the App.js for example:
function App() {
const [user, setUser] = useState([]);
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
if (authUser) {
setUser(authUser);
} else {
setUser(false);
}
})
}, [])
return (
< div >
<Router>
<Switch>
<Route path = "/SomePath">
<SomeLocation user={user}/>
</Route>
</Switch>
</Router>
</div >
);
}
export default App;
and since the user have all the data you need to get any other piece of information (usually) you don't need to worry about nested collections however, what if I'm using nested collections ?
if you want to access all the data from a nested collection that's also fine you do not require any other extra information apart from the user
Ej:
useEffect(() => {
const dbRef= db.collection("users").doc(user.uid).collection("someCollection")
dbRef.onSnapshot(snapshot => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setDataSomewhere(tempData);
})
}, [user]);
However how you get the data of the uid of an specific document inside a nested location with just the user ? you can't (as far as I'm aware)
The Important bit without all the background
EJ:
if you have let's say a "parent" and he adds "students" which is my case:
and I wanted to edit this "student" that is in a nested collection, let's make an example on the first one the one named "alfonsa" this is the edit form which is in another "/Path"
The way I'm handling the data of the student is the following, when you check the student and you select the edit icon from the data-table it sends some data through useLocation()
//PATH-A (The one with the data table)
const editStudent= {
pathname: '/EDIT_STUDENT',
data: studentData
}
const editStudents= () => {
if(studentData== 0)
{
window.alert("Select a student")
}
else {
history.push(editStudent);
}
};
///EDIT_STUDENT (Edit form)
let studentData= useLocation();
let data = studentData.data;
console.log(data)
const uid = data[0].uid <- Here I get that student specific uid
This is what the console log returns (All correct data):
However all the data disappears on refresh (which makes sense because is no longer getting the useLocation data from the previous "/path" location) this is the question:
How can I keep/store/maintain that data that comes from the useLocation() on refresh
Any help/tip/example is welcome
Forgot to add this
This is basically what comes up when I refresh
UPDATE So I tried localStorage() as someone mention but now I can't access the data because is coming as a string how can I separate the data ?
This is how I'm storing the data:
useEffect(() => {
const localStorageEstData = window.localStorage.getItem("students");
localStorageEstData && setStudentsData(JSON.parse(localStorageEstData));
}, []);
useEffect(() => {
window.localStorage.setItem("students", JSON.stringify(studentsData));
}, [studentsData]);
This is how I'm getting the data + how it shows in console:
let data = window.localStorage.getItem("estudiantes")
is coming as a whole string how can I separate it ?
Since no one posted an answer I will based on what #Daniel Beck said
Instead of using useLocation() that is to pass data from one /Path to another is better to use localStorage for multiple reasons and the more important one is to persist/keep data on refresh.
if you use useLocation() and you refresh the data will disappear because is no longer parsing data from one /Path to another. However the localStorage will persist on refresh which is why is more useful for this case
simple solution for me was:
useEffect(() => {
const localStorageEstData = window.localStorage.getItem("students");
localStorageEstData && setStudentsData(JSON.parse(localStorageEstData));
}, []);
useEffect(() => {
window.localStorage.setItem("students", JSON.stringify(estudiantesData));
}, [studentsData]);
and then in the other end:
const data = JSON.parse(window.localStorage.getItem("students"))
const uid = data[0].uid
that way I have access to all the information not just the uid

Why react state (useState) is updated but not updated when log it?

Hi am trying to create a simple multi file component, But the files state is not behaving as expected.
The FileUpload component works fine and when the user chooses a file and it starts uploading the onStart prop method is called. And then when it finishes successfully the 'onFinish' prop method is called. This code seems fine to the point of consoling the object in the onfinish method. it consoles an old value of the object before it was modified by the onStart Method. I expected the file object value in the console to include the buffer key since it was added when the onStart method was called but it's not there.
Example initial state files should be [] when the use effect is called on the state files should be updated to [{_id:"example_unique_id"}] then a button for upload will appear and when user chooses a file and onStart modifies the object and the state should be updated to [{_id:"example_unique_id", buffer:{}] and finally when it finishes files should be [{_id:"example_unique_id", buffer:{}] but instead here it returns [{_id:"example_unique_id"}].
What could I be missing out on?
Also, I have React Dev tools installed and it seems the state is updated well in the dev tools.
import React, { useState } from 'react'
import { useEffect } from 'react';
import unique_id from 'uniqid'
import FileUpload from "./../../components/FileUpload";
const InlineFileUpload = ({ onFilesChange }) => {
const [files, setFiles] = useState([]);
function onFinish(file, id) {
const old_object = files.filter((file) => file._id == id)[0];
console.log("old object on after upload", old_object);
}
const addFile = (file, id) => {
const old_object = files.filter((file) => file._id == id)[0];
const index = files.indexOf(old_object);
const new_files = [...files];
new_files.splice(index, 1, { ...old_object, buffer: file });
setFiles(new_files);
};
useEffect(() => {
const new_attachments = files.filter(({ buffer }) => buffer == undefined);
if (new_attachments.length == 0) {
setFiles([...files, { _id: unique_id() }]);
}
const links = files.filter((file) => file.file !== undefined);
if (links.length !== 0) {
onFilesChange(links);
}
}, [files]);
return (
<>
{files.map((file) => {
const { _id } = file;
return ( <FileUpload
key={_id}
id={_id}
onStart={(e) => addFile(e, _id)}
onFinish={(e) => onFinish(e, _id)}
/>
);
})}
</>
);
};
export default InlineFileUpload
I think the problem is caused by the fact that your this code is not updating the state:
const addFile = (file, id) => {
const old_object = files.filter((file) => file._id == id)[0];
const index = files.indexOf(old_object);
const new_files = [...files];
new_files.splice(index, 1, { ...old_object, buffer: file });
setFiles(new_files);
}
files looks like an array of objects.
Spread operator will not do a deep copy of this array. There are a lot of examples on the internet, here is one.
let newArr = [{a : 1, b : 2},
{x : 1, y : 2},
{p: 1, q: 2}];
let arr = [...newArr];
arr[0]['a'] = 22;
console.log(arr);
console.log(newArr);
So your new_files is the same array. Splice must be making some modifications but that is in place. So when you are doing this setFiles(new_files);, you are basically setting the same reference of object as your newState. React will not detect a change, and nothing gets updated.
You have the option to implement a deep copy method for your specific code or use lodash cloneDeep.
Looking at your code, this might work for you : const new_files = JSON.parse(JSON.stringify(files)). It is a little slow, and you might lose out on properties which have values such as functions or symbols. Read
The reason you are getting the old log is because of closures.
When you do setFiles(new_files) inside addFiles function. React updates the state asynchronously, but the new state is available on next render.
The onFinish function that will be called is still from the first render, referencing files of the that render. The new render has the reference to the updated files, so next time when you log again, you will be getting the correct value.
If it's just about logging, wrap it in a useEffect hook,
useEffect(() => {
console.log(files)
}, [files);
If it's about using it in the onFinish handler, there are answers which explore these option.

How do I get data from the async/await function before rendering out the UI?

I am working out on Github API and using an async/await function to get a list of user's repository.
The problem I am facing is, I am using a Dropdown component where I have to provide an array full of repo list which will work as a dropdown, and that array data is coming from the async/await function.
const list = []
async function fetchRepos() {
await octokit.repos
.listForUser({
username: 'abhinav-anshul',
})
.then((details) => list.push(details.data[0].name))
console.log('List Array', list)
console.log([...list])
}
Therefore, the UI renders before the function gets executed, and the items array turns out to empty.
<>
<DropDown items={[...list]} placeholder='Select a Repository' />
</>
I am using the spread operator to get data in my Dropdown component
How do I make sure that I do get data in the items prop from the async-await function, I believe the function cannot be synchronous as well since it is handling the API request. What could be a good strategy for this problem?
Any help is really appreciated.
Thank You
You could initialise your list data with a falsy value, say null and use a ternary operator to stop the Dropdown UI from rendering until your API call finishes executing and populates your list data with the array. This would be 'truthy' value and then your Dropdown could be rendered.
You'll have to use useState and useEffect to achieve this if you are using hooks based components. It will look something like this
const [list, setList] = useState([]);
async function fetchRepos() {
await octokit.repos
.listForUser({
username: 'abhinav-anshul',
})
.then((details) => {
setList((prevState) => {
prevState.push(details.data[0].name);
return prevState;
});
});
console.log('List Array', list);
console.log([...list]);
}
useEffect(() => {
(async () => {
await fetchRepos();
})();
}, []);
Can try below solution
const [isShowDropdown, setDropdown] = useState(false);
const list = []
async function fetchRepos() {
await octokit.repos
.listForUser({
username: 'abhinav-anshul',
})
.then((details) => list.push(details.data[0].name))
setDropdown(true)
console.log('List Array', list)
console.log([...list])
}
Render dropdown like this
<>
{isShowDropdown?<DropDown items={[...list] || [] } placeholder='Select a Repository' />:null}
</>

Add key/value pair to existing array of objects

I have an array of objects that is saved into a userList useState which is composed of:
[{
firstName: "blah"
lastName: "blah2"
}
{
firstName: "test"
lastName: "test2"
}]
I have a useEffect that calls a function and returns a value. I want to store a new key and value to each user in userList.
useEffect(() => {
userList.forEach((user, index) =>
returnNewValueForNewKeyFunction(user, index).then(newValue => {
userList[index]['newKey'] = newValue
//this console.log shows new field and value
console.log(userList)
//this console.log ALSO shows new field and value
console.log(JSON.stringify(contactList[index]))
})
)
}
}, [])
This is fine if I'm operating out of console.log, but unfortunately I need to render the data onto the page.. in my render I have:
return (
<TableBody>
{userList
.map((user, index) => (
<TableRow>
<TableCell>
{user.newKey}
</TableCell>
)
user.newKey is showing as blank and it seems like the user wasn't updated at all. How can I make it so the value is actually updated and can be read from when rendering?
You shouldnt mutate your list, you should use useState to store your list, so something like this :
const [ state, setState] = useState(userList);
Then when you want to update, do something like this :
const listCopy = [...state];
//Logic to update your list here
listCopy[index][otherindex] = Value;
setState(listCopy)
Hope this helps
You are modifying your userList but not calling your set function on which means React won't know to re-render with the updated state.
Instead of mutating the current state, you should create a new array and then call the set function returned by useState with the updated array after making your changes.
It also looks like your returnNewValueForNewKeyFunction is a promise / async which means each of your item changes are happening async. You'll need to make these synchronous / wait for them all before updating your state to make your state change a single update for the UI.
E.g., putting these both together - if you are doing:
const [userList, setUserList] = useState();
You could do:
useEffect(() => {
// Since can't use an async func directly with useEffect -
// define an async func to handle your updates and call it within the useEffect func
const updateUsers = async () => {
// Create a new array for your updated state
const updatedUserList = [];
// Loop over your values inline so your can await results to make them sync
for (let index = 0; index < userList.length; index ++) {
const user = userList[index];
const newVal = await returnNewValueForNewKeyFunction(user, index);
// Create a shallow copy of the original value and add the newValue
updatedUserList[index] = { ...user, newKey: newValue };
// ... Any other logic you need
}
// Call set with the updated value so React knows to re-render
setUserList(updatedUserList);
};
// Trigger your async update
updateUsers();
}, [])

Categories

Resources