pass dynamic values in API params coming from navigation in react native - reactjs

I got following 2 values in id and company variable by navigating the screen.
useEffect(() => {
if (props.route && props.route.params) {
console.log("id-->", props.route.params.oved);
console.log("company-->", props.route.params.company);
}
});
e,g i got 2 values like this
id--> 31
company--> 465
I want to pass the id and company value in API params.
api.js : -
const AllFormCardAPI = () => {
const [formAllAPIData, setAllFormAPIData] = useState("");
//NOTE: retrieving loginAuthToken from store
const loginAuthToken = useSelector(
(state) => state.loginAuthTokenReducer.loginAuthToken
);
useEffect(() => {
axios
.get(GET_ALL_FORM, {
//TODO: take parameters from user data currently parameters are static
params: {
company: "984",
employee: "38887683",
DisplayRow: "123456",
},
headers: {
Authorization: `Bearer ${loginAuthToken}`,
},
})
.then((response) => response.data)
.then((data) => setAllFormAPIData(data))
.catch((error) => {
if (error.status === 401) {
//NOTE: handling token expire
return ExpireAlertRestart();
} else {
Alert.alert(error.message);
}
})
.finally(() => console.log("finally block all form api", formAllAPIData));
}, []);
};
i want to pass those 2 values i,e id and company from navigation which I mentioned above and those has to be passed as string to following in API params.
My new API params should look like this. The id value should replace in employee params and company value should replace in company params.
params: {
company: "465",
employee: "31",
action.js:--
import { CHANGE_SELECTED_COMPANY } from "./action-constants";
export const changeCompany = (updatedCompany, updatedId) => {
return {
type: CHANGE_SELECTED_COMPANY,
updatedCompany,
updatedId,
};
};
reducer.js:--
import { CHANGE_SELECTED_COMPANY } from "../actions/action-constants";
const initialState = {
company: "",
id: "",
};
const changeCompanyReducer = (state = initialState, action) => {
switch (action.type) {
case CHANGE_SELECTED_COMPANY:
return {
company: {
company: action.updatedCompany,
id: action.updatedId,
},
};
}
return state;
};
export default changeCompanyReducer;
congigure-store.js:--
import changeCompanyReducer from "./reducers/change-company-reducer";
const rootReducer = combineReducers({changeCompanyReducer});
How can i store the update values getting from navigation in Redux?
could you please write code for redux??

const AllFormCardAPI = (props) => {
//New lines
const id = props?.route?.params?.oved;
const company = props?.route?.params?.company;
//New lines end
const [formAllAPIData, setAllFormAPIData] = useState("");
//NOTE: retrieving loginAuthToken from store
const loginAuthToken = useSelector(
(state) => state.loginAuthTokenReducer.loginAuthToken
);
useEffect(() => {
axios
.get(GET_ALL_FORM, {
//TODO: take parameters from user data currently parameters are static
params: {
company: company,
employee: id,
DisplayRow: "123456",
},
headers: {
Authorization: `Bearer ${loginAuthToken}`,
},
})
.then((response) => response.data)
.then((data) => setAllFormAPIData(data))
.catch((error) => {
if (error.status === 401) {
//NOTE: handling token expire
return ExpireAlertRestart();
} else {
Alert.alert(error.message);
}
})
.finally(() => console.log("finally block all form api", formAllAPIData));
}, []);
};

Related

Reactjs - Firebase : Cancel Old Requests

I'm new to Firebase Realtime Database, and i'm trying to implement a search field that allow users to search for other users and view their profiles.
The Problem Is:
I want to make the search realTime(on each input change).but whenever a new request's sent, the old request is still working in the backend which's causing unexpected behavior,i've wrapped this functionality in a useEffect Hook,old sideEffects has to be cleaned up to make the query results predictable,how can i abort the previous request.
useSearchOwner Custom Hook:
const useSearchOwner = () => {
const [{ SearchValue, SearchResult, Search }, dispatch] = useReducer(
reducer,
{
SearchValue: "",
SearchResult: "",
Search: false,
}
);
const isFirstRender = useRef(true);
const onChangeHandler = (e) =>
dispatch({
type: ACTIONS.UPDATE_SEARCH_VALUE,
payload: { searchValue: e.target.value },
});
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
dispatch({ type: ACTIONS.START_SEARCHING });
const DispatchQueryByResult = async () => {
const ArrayOfOwners = await FirebaseUtilityInstance.SearchOwnerResult(
SearchValue
);
dispatch({
type: ACTIONS.UPDATE_SEARCH_RESULT,
payload: { searchResult: ArrayOfOwners },
});
dispatch({ type: ACTIONS.STOP_SEARCHING });
return () => {
FirebaseUtilityInstance.SearchOwnerCleanup();
};
};
DispatchQueryByResult();
}, [SearchValue]);
useEffect(() => {
console.log(SearchResult);
}, [SearchResult]);
return {
onChangeHandler: onChangeHandler,
Query: SearchValue,
QueryResult: SearchResult,
isSearching: Search,
};
};
Firebase Method To Do Query:
SearchOwnerResult = async (Query) => {
const { firstName, lastName } = getFirstNameAndLastName(Query);
let ArrayOfOwners = [];
await this.Database()
.ref("users")
.orderByChild("UserType")
.equalTo("owner")
.once("value", (snapshot) => {
const OwnersContainer = snapshot.val();
const keys = Object.keys(OwnersContainer);
for (let i = 0; i < keys.length; i++) {
const CurrentOwner = OwnersContainer[keys[i]];
if (
CurrentOwner.FirstName === firstName ||
CurrentOwner.LastName === lastName
) {
ArrayOfOwners.push(OwnersContainer[keys[i]]);
}
}
});
return ArrayOfOwners;
};

Accessing the values from Promise inside useEffect hook

const useOnfidoFetch = (URL) => {
useEffect(() => {
const appToken = axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const id = json_data.applicant_id;
const token = json_data.onfido_sdk_token;
return {id, token};
});
if (appToken) {
console.log('this is working!');
OnfidoSDK.init({
// the JWT token you generated above
token: null,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}
}, [URL]);
}
export default function() {
const URL = `${transmitAPI}/anonymous_invoke?aid=onfido_webapp`;
const result = useOnfidoFetch(URL, {});
return (
<div id={onfidoContainerId} />
);
}
I have refactored this dozens of times already, I am getting back some values from the appToken Promise, but I need to provide the token value from that Promise to that token property inside of Onfido.init({}) and I need to provide the id to the applicant_id property and I continue to get undefined.
If you need the token for something else as well, then i would suggest storing it in useState, and triggering OnfidoSDK.init when the value of that state changes.
Like this:
const useOnfidoFetch = (URL) => {
const [token, setToken] = useState();
useEffect(() => {
axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const token = json_data.onfido_sdk_token;
setToken(token);
})
}, [URL])
useEffect(() => {
if (!token) return;
OnfidoSDK.init({
// the JWT token you generated above
token,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}, [token]);
}
Move the entire if(appToken){ ... } inside the body of the second .then((json_data) => { ... })
Something like this:
const useOnfidoFetch = (URL) => {
useEffect(() => {
const appToken = axios.get('http://localhost:5000/post_stuff')
.then((response) => response.data.data.data.json_data)
.then((json_data) => {
const id = json_data.applicant_id;
const token = json_data.onfido_sdk_token;
// Here. This code will be executed after the values are available for id and token
if (appToken) {
console.log('this is working!');
OnfidoSDK.init({
// the JWT token you generated above
token: null,
containerId: "root",
steps: [
{
type: 'welcome',
options: {
title: 'Open your new bank account',
},
},
'document'
],
onComplete: function (data) {
console.log('everything is complete');
axios.post('https://third/party/api/v2/server-api/anonymous_invoke?aid=onfido_webapp', {
params: {
applicant_id: appToken.applicant_id
}
});
}
});
}
return {id, token};
});
// In here the promise is not yet finished executing, so `id` and `token` are not yet available
}, [URL]);
};
export default function() {
const URL = `${transmitAPI}/anonymous_invoke?aid=onfido_webapp`;
const result = useOnfidoFetch(URL, {});
return (
<div id={onfidoContainerId} />
);
}
For better readability, you could also move the if(appToken){ ... } block inside a separate function that takes id, token as arguments, which you can call from inside the promise.then block.

2 axios request problem rendering before geting data from request

I am building a weather app similar to AccuWeather in React.js.
I have a problem with axios.get request. I need to get data from 2 URLs. The second request (forecast) is bigger. Because of this when I want to get given city weather forecast in a certain parts of the app, DOM is rendered before I get data from Axios request.
I was trying to use async and await in my code but without success. Below you can find function getCityWeather which contain those 2 Axios request. Any idea?
getCityWeather = event => {
axios
.get(
"https://api.openweathermap.org/data/2.5/weather?q=" +
this.state.city +
"&units=metric&appid=" +
this.state.appiID
)
.then(weather => {
console.log(weather);
this.setState({
currentCityWeather: weather.data.weather,
currentCityName: weather.data.name,
currentCityCountry: weather.data.sys.country,
CityWeatherMain: weather.data.main,
CityWeatherWind: weather.data.wind,
CityWeatherClound: weather.data.clouds,
cityLoaded: true
});
})
.catch(error => {
console.log(error);
});
axios
.get(
"https://api.openweathermap.org/data/2.5/forecast?q=" +
this.state.city +
"&units=metric&appid=" +
this.state.appiID
)
.then(forecast => {
console.log(forecast);
this.setState({
CityForecastDataList: forecast.data.list,
cityLoaded: true
});
})
.catch(error => {
console.log(error);
});
event.preventDefault();
};
Try code bellow.
const baseUrl = 'https://api.openweathermap.org/data/2.5';
const getCityWeather = (event) => {
event.preventDefault();
const { city, appiID } = this.state;
const urls = [
`${baseUrl}/weather?q=${city}&units=metric&appid=${appiID}`,
`${baseUrl}/forecast?q=${city}&units=metric&appid=${appiID}`,
];
const promises = urls.map(s => axios.get(s));
Promise.all(promises)
.then(([weatherResponse, forecastResponse]) => {
const {
date: {
weather,
name,
sys: { country },
main,
wind,
clouds,
},
} = weatherResponse;
const {
data: { list },
} = forecastResponse;
this.setState({
currentCityWeather: weather,
currentCityName: name,
currentCityCountry: country,
CityWeatherMain: main,
CityWeatherWind: wind,
CityWeatherClound: clouds,
CityForecastDataList: list,
cityLoaded: true,
});
})
.catch(({ message }) => {
console.error(message);
});
};
Thanks for answer Kirill,
I tried to implement your code but get
ReferenceError: Cannot access 'promises' before initialization
getCityWeather
src/containers/WeatherData/WeatherData.js:39
36 | `${baseUrl}/weather?q=${city}&units=metric&appid=${appiID}`,
37 | `${baseUrl}/forecast?q=${city}&units=metric&appid=${appiID}`
38 | ];
> 39 | const promises = urls.map(s => axios.get(s),
| ^ 40 |
41 | Promise.all(promises)
42 | .then(([weatherResponse, forecastResponse]) => {
Below please find my implementation to class based component which I use
getCityWeather = (event) => {
event.preventDefault();
const baseUrl = 'https://api.openweathermap.org/data/2.5';
const { city, appiID } = this.state;
const urls = [
`${baseUrl}/weather?q=${city}&units=metric&appid=${appiID}`,
`${baseUrl}/forecast?q=${city}&units=metric&appid=${appiID}`
];
const promises = urls.map(s => axios.get(s));
Promise.all(promises)
.then(([weatherResponse, forecastResponse]) => {
const {
date: {
weather,
name,
sys: { country },
main,
wind,
clouds
},
} = weatherResponse;
const {
data: { list },
} = forecastResponse;
this.setState({
currentCityWeather: weather,
currentCityName: name,
currentCityCountry: country,
CityWeatherMain: main,
CityWeatherWind: wind,
CityWeatherClound: clouds,
CityForecastDataList: list,
cityLoaded: true,
});
})
.catch(({ message }) => {
console.error(message);
})
)}

Trying to modify a data from a React Promise Response changes globally

I have created a codesandbox with a simplified version of my problem
https://codesandbox.io/s/new-react-context-api-ei92k
I get something from a fetch (in this case a user)
I then create a local copy of this user and make some changes to it
The problem: Any changes update my initial user object
Can someone tell me how this is possible? and how can I avoid this?
import React, { useState, useEffect } from "react";
import { AppSessionContext } from "./AppContext";
import Header from "./Header";
const user = {
userName: "jsmith",
firstName: "John",
lastName: "Smith",
isAdmin: true
};
const loadProfile = () => Promise.resolve(user);
function createUserWithNewName(userToUpdate) {
userToUpdate["userName"] = "Dummy";
return userToUpdate;
}
const App = () => {
const [user, setUser] = useState({});
const [Loaded, setLoaded] = useState(false);
var amendedUser = {};
useEffect(() => {
loadProfile()
.then(user => {
setUser(user);
console.log(user);
})
.then(() => {
amendedUser = createUserWithNewName(user);
console.log(amendedUser);
console.log(user);
})
.then(setLoaded(true));
}, []);
if (!Loaded) {
return "Loading";
}
return (
<AppSessionContext.Provider value={{ user }}>
<div className="App">
<Header />
</div>
</AppSessionContext.Provider>
);
};
export default App;
snippet of production code
loadTableDefault() {
fetch(defaultUrl(), {method: 'GET'})
.then(res => res.json())
.then(response => {
this.setState({
data: response,
})
return response
})
.then(response => {
this.setState({
table_data: formatResponsePretty(response),
})
})
.catch(error => console.error('Error:', error));
}
formatResponsePretty
export function formatResponsePretty(oldData) {
const newData = {
...oldData,
};
// consider re-writting the flask response to this format
const obj = { allocations: [] };
var theRemovedElement = ''
var ports = []
ports = Object.values(newData['allocations']['columns']);
ports.shift();
var dataArray = ['allocations', 'conditions', 'liquidity', 'hedging']
for (const index of dataArray) {
for (const i of newData[index]['data']) {
theRemovedElement = i.shift();
if (index === 'allocations') {
obj[index][theRemovedElement] = i
}
else {
obj[theRemovedElement] = i;
}
}
}
const rows = []
let index = 0;
Object.keys(obj).forEach(element => {
index = formatting.findIndex(x => x.name === element)
if (formatting[index] && formatting[index]['type'] === 'number') {
var new_obj = obj[element].map(function (el) {
return Number(el * formatting[index]['multiplier']).toFixed(formatting[index]['decimal']) + formatting[index]['symbol']
})
rows.push(new_obj)
}
else if (formatting[index] && formatting[index]['type'] === 'string') {
rows.push(obj[element])
}
else if (formatting[index] && formatting[index]['type'] === 'boolean') {
// there should be logic here to display true or false instead of 1 and 0
// this could be in the upload
rows.push(obj[element])
}
else {
rows.push(obj[element])
}
})
const arrOfObj = createRecords(ports, rows)
return {obj: obj, ports: ports, rows: rows, arrOfObj: arrOfObj}
}
In createUserWithNewName() you are updating the original user object and returning it.
You instead want to create a new object with all the old user properties, but with just the username changed. Thankfully, object destructuring makes this super easy:
function createUserWithNewName(oldUser) {
const newUser = {
...oldUser,
userName: 'Dummy',
};
return newUser;
}
This will copy all the properties of oldUser to a new object and then just update userName!
You're also going to want to pass user down to that second .then() as it won't currently be available in there:
.then(user => {
setUser(user);
console.log(user);
return user;
})
.then(user => {
amendedUser = createUserWithNewName(user);
console.log(user, amendedUser);
})
Update CodeSandbox link: https://codesandbox.io/s/new-react-context-api-tgqi3

React project- chatting app. Need advice on setting interval

below is my main code for my project, which is a simple open chatting room.
This is a chatting client basically, which can only get chatting logs and post chats.
To log in, I request for an authorization key to the server and use that key to start chatting.
The thing that I want to do is to get the chatting logs from the server every 3 seconds.
So, after it retrieves chats every 3 seconds, the page should refresh with new chats.
However, this seems not to be working well for me.
If there is a good React pro who can solve this problem for me, please do.
import React from 'react';
import { withRouter } from 'react-router-dom';
import './Chat.css';
import InfoBar from '../InfoBar/InfoBar';
import Input from '../Input/Input';
import Messages from '../Messages/Messages';
const API_ENDPOINT = 'https://snu-web-random-chat.herokuapp.com';
let INTERVAL_ID;
const Chat = ({ token, setToken }) => {
const [ name, setName ] = React.useState('');
const [ message, setMessage ] = React.useState('');
const [ messages, setMessages ] = React.useState([]);
const [ lastEl, setLastEl ] = React.useState({});
React.useEffect(() => {
if (localStorage.getItem('username')) {
setName(localStorage.getItem('username'));
}
window.addEventListener('scroll', listenToScroll, true);
fetchData();
INTERVAL_ID = setInterval(() => {
fetchData();
}, 3000);
return () => {
window.removeEventListener('scroll', listenToScroll);
clearInterval(INTERVAL_ID);
};
}, []);
const fetchData = () => {
fetch(`${API_ENDPOINT}/chats?createdAtFrom=${lastEl.createdAt || ''}`)
.then((res) => res.json())
.then((msgs) => {
const updatedMsgs = messages.concat(msgs);
setLastEl(msgs[msgs.length - 1]);
setMessages(updatedMsgs);
});
};
const listenToScroll = (e) => {
const div = document.getElementById('messagesContainer');
// console.log(window.scrollY);
// const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
// const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
// const scrolled = winScroll / height;
};
const onLogin = (e) => {
e.preventDefault();
fetch(`${API_ENDPOINT}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `name=${name}`
})
.then((response) => response.json())
.then(({ key }) => {
if (key) {
setToken(true);
localStorage.setItem('__key', key);
localStorage.setItem('username', name);
}
})
.catch((err) => console.error(err));
};
const sendMessage = (e) => {
e.preventDefault();
if (message) {
fetch(`${API_ENDPOINT}/chats`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Key ${localStorage.getItem('__key')}`
},
body: `message=${message}`
})
.then((response) => response.json())
.then((res) => {
const obj = {
createdAt: res.createdAt,
message: res.message,
userName: res.user.name,
_id: res.user._id
};
setMessages([ ...messages, obj ]);
})
.catch((err) => console.error(err));
setMessage('');
}
};
const logout = () => {
localStorage.removeItem('__key');
localStorage.removeItem('username');
setToken(false);
};
const loadMessages = () => {
fetchData();
clearInterval(INTERVAL_ID);
};
return (
<div className="outerContainer">
<div className="innerContainer">
<InfoBar
name={name}
token={token}
handleInput={(e) => setName(e.target.value)}
handleLogin={onLogin}
handleLogout={logout}
/>
<Messages messages={messages} name={name} lastEl={lastEl} loadMore={loadMessages} />
{token && <Input message={message} setMessage={setMessage} sendMessage={sendMessage} />}
</div>
</div>
);
};
export default withRouter(Chat);
I can give u the advice to make a chat app with socket.io, not setInterval.
https://socket.io/get-started/chat

Resources