ReactJS: Update values without reload the page - javascript

I have this problem, when I do a insert or a change about some data, to see the new data I need to reload the page while I would to update automatically the value without the need to reload the page. How can I do?
This is the part where the user click on submit and the post
_onSubmit(Document)
{
const self = this
if ( !_.isEmpty(Document) )
{
//..
if (Document && !_.isEmpty(Document.Anagraphics))
{
alertify.confirm(
utility.t('sureYouWanna_SAVE'),
() => {
const now = new Date();
Document._id = `PRODUCT:${new Date().getTime()}-${utility.CUID()}`
Document.CreationDate = now.toISOString()
Document.CategoryCode
Document.Status = 'New';
Document.Type = 'PRODUCT';
self._POST(Document)
},
function(){}
).set('labels', {ok: utility.t('YES_SAVE'), cancel: utility.t('CANCEL')})
}
else
{
$methods.WarnMissingValues()
}
}
else {
$methods.WarnMissingValues()
}
}
_POST(Document)
{
console.log("DOCUMENT POST", Document)
const self = this
const auth = this.props.db.auth
fetch(`${this.props.db.couch_db_host_url}requests`,{
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa(`${auth.username}:${auth.password}`)
},
body: JSON.stringify(Document)
})
.then(response => {
alertify.dismissAll()
if(response.status > 299 || response.status < 200){
alertify.error(utility.t('AN_ERROR_OCCURRED'))
self._updateState({ submitSucceeded: false })
}
else{
alertify.alert(utility.t('ITEM_EDITED_OK'), function(){})
self.props.history.push({
pathname: RoutesIT.products_details
})
}
})
.catch((err, warning) => {
if (err)
{
alertify.dismissAll()
alertify.error(utility.t('AN_ERROR_OCCURRED'))
console.log('_POST', err);
self._updateState({ submitSucceeded: false })
}
else
{
console.log(warning)
alertify.dismissAll()
alertify.warning(utility.t(warning))
}
})
}
How can I do to not reload the page to see the result of the post? Thank you
UPDATE:
In the page I have also:
function mapStateToProps(state) {
const { app: { login, p, c, l, c_timestamp, p_timestamp, l_timestamp }, form } = state;
return {
db: login ? login.db : null,
Sender: login ? login.Location : null,
timestamp: login ? login.timestamp : null,
[ FORM_NAME ]: form[FORM_NAME],
products: p,
locations: l,
categories: c,
categories_timestamp: c_timestamp,
products_timestamp: p_timestamp,
locations_timestamp: l_timestamp,
utente: login,
};
}
while the reducers
case actions.CATE_UPDATE:
{
return {
...state,
c: action.payload,
c_timestamp: new Date().getTime()
}
}

For what I can see in your code, the problem may lie in the fact that you're not dispatching any action when you submit the data.
Redux store can only be modified via actions, and since you're not triggering any, its contents are never being updated. This explains why your component is not updated in real time: your local data is never changing, so React is not aware of any updates. Things works when you reload the page because you're probably fetching the data from server, where the data did change during your POST request.
In order to fix this issue, you first need to pass a mapDispatchToProp to the your component, same as what you did with mapStateToProps:
connect(mapStateToProps, mapDispatchToProps)(YourComponent);
Inside of mapDispatchToProps, you have to return a property containing a function that will dispatch the CATE_UPDATE action you want to run:
const mapDispatchToProps = (dispatch) => ({
cateUpdateAction: (payload) => dispatch({
type: CATE_UPDATE,
payload
}),
});
Once you've done that, you'll be able to access this function from your component's props and call it inside of your _POST method.
if (response.status > 299 || response.status < 200){
alertify.error(utility.t('AN_ERROR_OCCURRED'))
self._updateState({ submitSucceeded: false })
} else {
alertify.alert(utility.t('ITEM_EDITED_OK'), function(){})
// Dispatch action to update data in Redux store
self.props.cateUpdateAction(data_to_save);
self.props.history.push({
pathname: RoutesIT.products_details
})
}

Related

Updated object not being returned properly in nextjs

So basically I'm working on a nextjs app which uses authentication. I have a 2 functions which I run on every page load. The first checks if jwt cookies exist and calls another function to validate the tokens if they don't exist. This function is ran from wrapper.getServerSideProps and is passed in the context as ctx. This function works as intended.
export const checkServerSideCookie = (ctx) => {
const access = getCookie("access", ctx.req);
const refresh = getCookie("refresh", ctx.req);
if (access && refresh) {
return checkAuthentication(access, refresh);
} else return { isAuthenticated: false, token: null };
};
The second function is the token validator and this is where the issue arises. I have an object which I intended to update if the validation is successful and leave alone if it isn't. Here is the function
export const checkAuthentication = (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null,
};
const body = JSON.stringify({ token: access });
axios
.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
obj.isAuthenticated = true;
obj.token = access;
})
.catch((err) => {
// call new token function using refresh
console.log("it doesnt work");
});
return obj;
};
The issue is is that the .then does update the object, and when I console.log(obj) in the .then it shows the proper obj to return, however when I return the obj it still holds the initial values of false and null. I don't understand what the issue is. I try doing the return in the .then itself but it throughs this error
TypeError: Cannot destructure property 'isAuthenticated' of 'Object(...)(...)' as it is undefined.
What is the issue here? It all seems good but the updated obj isn't returned.
axios.post is async, you're returning the obj before it gets filled with data from the api response, you can use async/await to solve that :
export const checkAuthentication = async (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null
};
const body = JSON.stringify({ token: access });
try {
const res = await axios.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json"
}
});
obj.isAuthenticated = true;
obj.token = access;
} catch (e) {
// do something with the error
// call new token function using refresh
console.log("it doesnt work");
}
return obj;
};
usage (checkAuthentication now return a promise ) :
checkAuthentication(a, b).then((obj) => {
console.log(obj);
});
When you call checkAuthentication it immediately returns the obj with the default properties. You have an asynchronous operation specified in your function, however you don't wait until it's done. You'd have to rebuild your function the following way:
export const checkAuthentication = (access, refresh) => {
const obj = {
isAuthenticated: false,
token: null,
};
const body = JSON.stringify({ token: access });
return new Promise((resolve, reject) => {
axios
.post("http://localhost:8000/api/jwtoken/verify/", body, {
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
resolve({
isAuthenticated: true,
token: access
})
})
.catch((err) => {
// call new token function using refresh
console.log("it doesnt work");
reject();
});
});
};
and then call your function the following way:
checkAuthentication(access, refresh)
.then(console.log)
.catch(console.log)
You, of course, have multiple options to make your function cleaner, such as by using async/await etc, but this should give you a quick overview of what is wrong.

Vue, fetch returns empty array

I'm fetching some data in my vue-cli project.
I'm using Vuex to store the data.
It all runs successfully apart from the fact that I get an empty array, I have checked in Postman, and it works perfectly.
As you can see in my actions i had my commit in the if statement, currently commented out and moved. But when run in there I get a Promise returned. And as the current edition of my code I get an empty array.
I really cant see what my error is, so my best bet is you guys are able to see what I'm missing.
First I have my actions:
export default {
async getProLanguages({ commit }) {
commit(C.PROLANGAUGE_DATA_PENDING);
try {
const res = await fetch('https://dev-webapp-kimga5xexrm3o.azurewebsites.net/api/ProLang', {
method: 'GET',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer xxx'
}
});
if (res.status === 200) {
console.log(res);
// commit(C.PROLANGAUGE_DATA_SUCCESS, JSON.stringify(res.json()));
}
else {
commit(C.PROLANGAUGE_DATA_NO_CONTENT);
}
console.log(res)
return commit(C.PROLANGAUGE_DATA_SUCCESS, JSON.stringify(res.json()));
}
catch (e) {
commit(C.PROLANGAUGE_DATA_FAILURE);
}
}
And my mutations:
/**
* Indicates that programming language has succeded
*
* #param state
* #param payload
*/
[C.PROLANGAUGE_DATA_SUCCESS](state, payload) {
state.programmingLanguages = { ...state.programmingLanguages, loading: false, error: false, noContent: false, items: payload }
},
And I have my default state, which is imported into state.js:
const getDefaultState = () => ({
programmingLanguages: {
loading: false,
error: false,
noContent: false,
items: [
{
id: undefined,
name: undefined
}
]
}
});
I call my action with a beforeRouteEnter:
beforeRouteEnter(to, from, next) {
store.dispatch('programmingLanguages/getProLanguages').then(() => {
next();
});
}
and finally in my component I import mapState from Vuex:
computed: {
...mapState({
prolangs: state => state.programmingLanguages.programmingLanguages.items
})
}
I think something like items = await res.json(), then committing items could be a way forward (make sure all promises are resolved).

Global state in React Native in first load of the App

so im trying to make a App that loads all the thing in the loading screen/splash/auth screen.
So I in the basic I have a Welcome, Login and Home screen for now.
Welcome will be showing if the App is open for first time, Login if the user is opening the App without login or is logged out before closing the App and Home will be open if the user is logged in.
Here is the simply check:
componentDidMount() {
AsyncStorage.getItem("alreadyLaunched").then(value => {
if (value == null) {
AsyncStorage.setItem('alreadyLaunched', 'true'); // No need to wait for `setItem` to finish, although you might want to handle errors
this.setState({ firstLaunch: 'true' });
}
else {
this.setState({ firstLaunch: 'false' });
}
})
}
loadApp = async () => {
const userToken = await AsyncStorage.getItem('userToken')
setTimeout(
() => {
if (this.state.firstLaunch == 'true') {
this.props.navigation.navigate('Welcome')
} else {
if (userToken) {
this.props.navigation.navigate('App')
} else {
this.props.navigation.navigate('SignIn')
}
}
}, 0
);
}
And if the login is correct I just put this on Async:
AsyncStorage.setItem("userToken", "logged");
This for now its working perfectly, but I need to get 3-4 for functions to get information to the server then State It. Here is one of the functions:
getSignalsCount = async (username, password) => {
try {
var DeviceInfo = require('react-native-device-info');
//AUTH
fetch(Config.SERVER_URL + '/mob/auth', {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "1",
id: "",
sessId: "",
data: {
u: username,
p: password,
}
})
}).then((response) => response.json())
.then((res) => {
let res_code = res['code'];
let session = "";
let cl_id = 0;
let name = "";
if (res_code == 51) {
session = res['session'];
cl_id = res["data"]["response"]["client_id"];
name = res["data"]["response"]["name"];
this.setState({fullName:name});
//GET STATS
fetch(Config.SERVER_URL + '/mob/sport/getSignalsInfo', { //home
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
code: "9",
sessId: session,
data: {
devId: '1234567890',
devName: DeviceInfo.getUniqueID(),
u: username,
client_id: cl_id,
type: {
nums: 50000,
period: 12
}
}
})
}).then((response) => response.json())
.then((res) => {
var closed = res["data"]["response"]["data"]["closed"];
var opened = res["data"]["response"]["data"]["open"];
this.setState({ total_active_signals: opened, total_closed_signals: closed })
})
.done();
}
})
.done()
}
};
So if set this function on the Auth.js and then use it on the same screen with {this.state.total_active_signals} will show me the varible.
But I need it to be show also on HOME or maybe LOGIN and other pages that I may created in future. So I basic need this state to be use on maybe every screen.
I tried to create a global.js with:
module.exports = {
username: '',
};
And then in HOME:
//.....
import global from '../../global'
//....
<Text>Username: {GLOBAL.username}</Text>
But the question now is how to fill the global.js with the state so that, Home/Login/Profile/Stuff screens to get it later.
Thanks!
You basically have two options:
Use Redux and create an app wide state (store) to which all
components have access to (recommended). Your idea of a global state
fits pretty good with the concept of redux.
Pass data as props between components. You can also use a navigation library
to deal with this (e.g. react navigation or react native router
flux)

Calling an API as a non-blocking call JavaScript

I am building a todo-list like feature which adds a task when Enter is pressed on an input task field. The Enter calls an API (add Task) which takes approx 200ms to execute. Since this is blocking call it hinders my code to execute fully and affects the usability of my system. Here is a code example of what I am trying to achieve.
handleChange (event) {
if (e.key === 'Enter') {
targetTaskId = e.target.getAttribute("data-downlink")
this.props.addTask(this.props.currentProject.id, '', '', taskId, this.props.currentTasks) //this function calls an add Task API which halts my system momentarily
targetSelector = targetTaskId
$('#' + targetSelector).focus()
this.setState({activeTask: targetSelector})
highlightActiveComponent(targetTaskId)
}
}
//addTask
export function addTask (project_id, taskName, taskNotes, upLink, taskList) {
console.log('Add Task API call', project_id, taskName, taskNotes, upLink)
return (dispatch) => {
callApi('tasks?projectId=' + project_id + '&name=' + taskName + '&notes=' + taskNotes + '&upLink=' + upLink, 'post')
.then(res => {
console.log('Response new task ', res)
let newTask = {name: res.name, id: res.id, notes: res.notes, upLink: upLink, projectId: project_id, assignee: 0, completed: 0, tags: [], isLiked: false, stories: [], likes: [], downLink: res.downLink}
let newTaskList = addTaskToTaskList(taskList, upLink, newTask)
dispatch(updateTasks({currentTasks: newTaskList}))
dispatch({ type: 'SET_ACTIVE_TASK_ID', payload: res.id })
})
}
}
//Fetch
export const API_URL = 'https://clients.rohan.axcelmedia.ca/v1'
export default function callApi (endpoint, method = 'get', body) {
let headers = {
'Accept': 'application/json',
'Content-Type' : 'application/json'
}
if (auth.loggedIn()) {
headers = _.merge(headers, {
Authorization: `Bearer ${auth.getToken()}`
})
}
return fetch(`${API_URL}/${endpoint}`, {
headers,
method,
body: JSON.stringify(body)
}).then(response => {
return response
}).then(response => response.json().then(json => ({ json, response })))
.then(({ json, response }) => {
if (!response.ok) {
return Promise.reject(json)
}
return json
})
.then(
response => response,
error => error
)
}
Add Task to tasklist
export function addTaskToTaskList(tasks, upLink, newTask){
updateTaskDownLink(tasks, newTask.upLink, newTask.id)
updateTaskUpLink(tasks, newTask.downLink, newTask.id)
if(upLink == 0){
tasks.unshift(newTask)
// console.log("Added in the start", tasks)
return JSON.parse(JSON.stringify(tasks))
}
let myIndex = getIndexOfTaskById(tasks, upLink)
console.log("Added the new task from helper", myIndex)
if (myIndex) {
console.log("Added the new task")
tasks.splice(myIndex + 1, 0, newTask);
// console.log("New Task List", JSON.parse(JSON.stringify(tasks)))
}
return JSON.parse(JSON.stringify(tasks))
}
export function updateTaskUpLink(tasks, taskId, upLink){
tasks.forEach(function(element, index) {
if(element.id == taskId) { element.upLink = upLink }
});
return tasks
}
export function updateTaskDownLink(tasks, taskId, downLink){
tasks.forEach(function(element, index) {
if(element.id == taskId) { element.downLink = downLink }
});
return tasks
}
My question is, is there anyway to call this API in a non-blocking fashion so that my code continues to execute and when the response from the api is received my cursor moves to the new task in a seamless manner.
Any help would be appreciated. Thankyou
[EDIT] : Added fetch function to demonstrate the async calls
You should use something like Fetch API for call the API in a non-blocking way:
fetch("/api/v1/endpoint/5/", {
method: "get",
credentials: "same-origin",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
}).then(function(response) {
return response.json();
}).then(function(data) {
console.log("Data is ok", data);
}).catch(function(ex) {
console.log("parsing failed", ex);
});
console.log("Ciao!");
The code that shows data in the snippet will be executed only when some data is returned by the server.
This means that in my example the log "Ciao!" will be showed before "Data is ok: ..."
Hope this helps :)
credits for the snippet: https://gist.github.com/marteinn/3785ff3c1a3745ae955c
First of all return JSON.parse(JSON.stringify(tasks)) is redundant, you can just return tasks right there, that will probably fix your speed problem alone. But incase it doesn't.
Your code might be blocking due to this type of thing here
tasks.forEach(function(element, index) {
if(element.id == taskId) { element.upLink = upLink }
});
return tasks
You iterate over the tasks array for updateTaskDownLink, again for updateTaskUpLink and probably again for getIndexOfTaskById, this is a lot of needless iteration.
Instead of searching through an array of tasks over and over, you should structure your tasks in a map
tasks = {
"someTaskId": {
id: "someTaskId",
upLink: "uplink stuff",
downLink: "downlink stuff"
}
}
This way when you go to update the task its really simple and really fast
tasks[taskId].upLink = upLink or tasks[taskId].downLink = downLink
No iterating, no blocking, no problem.
Also, this data structure will make getIndexOfTaskById obsolete! because you already have the key needed to access that task! Hooray!
If you're wondering how to iterate over your tasks structured as a map like that see here

415 (Unsupported Media Type) with REST Post request

I have a react component that when a checkbox is pressed, it calls a rest api, post request with a single parameter.
I put a breakpoint in the webapi and its never hit, still I get a 415 unsopported media type on the component
react js component (see onchange event)
import React, { Component } from 'react';
import { Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';
class ListTenants extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
fetchData = () => {
adalApiFetch(fetch, "/Tenant", {})
.then(response => response.json())
.then(responseJson => {
if (!this.isCancelled) {
const results= responseJson.map(row => ({
key: row.ClientId,
ClientId: row.ClientId,
ClientSecret: row.ClientSecret,
Id: row.Id,
SiteCollectionTestUrl: row.SiteCollectionTestUrl,
TenantDomainUrl: row.TenantDomainUrl
}))
this.setState({ data: results });
}
})
.catch(error => {
console.error(error);
});
};
componentDidMount(){
this.fetchData();
}
render() {
const columns = [
{
title: 'Client Id',
dataIndex: 'ClientId',
key: 'ClientId'
},
{
title: 'Site Collection TestUrl',
dataIndex: 'SiteCollectionTestUrl',
key: 'SiteCollectionTestUrl',
},
{
title: 'Tenant DomainUrl',
dataIndex: 'TenantDomainUrl',
key: 'TenantDomainUrl',
}
];
// rowSelection object indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
if(selectedRows[0].key != undefined){
console.log(selectedRows[0].key);
const options = {
method: 'post',
body: JSON.stringify({ clientid : selectedRows[0].key.toString() }) ,
config: {
headers: {
'Content-Type': 'application/json'
}
}
};
adalApiFetch(fetch, "/Tenant/SetTenantActive", options)
.then(response =>{
if(response.status === 200){
Notification(
'success',
'Tenant set to active',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Tenant not activated',
error
);
console.error(error);
});
}
},
getCheckboxProps: record => ({
type: Radio
}),
};
return (
<Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
);
}
}
export default ListTenants;
and the webapi method
[HttpPost]
[Route("api/Tenant/SetTenantActive")]
public async Task<IHttpActionResult> SetTenantActive([FromBody]string clientid)
{
var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
var allTenants = await tenantStore.Query().Where(x => x.TenantDomainUrl != null).ToListAsync();
foreach(Tenant ten in allTenants)
{
ten.Active = false;
await tenantStore.UpdateAsync(ten);
}
var tenant = await tenantStore.Query().FirstOrDefaultAsync(x => x.clientid == clientid);
if (tenant == null)
{
return NotFound();
}
tenant.Active = true;
var result = await tenantStore.UpdateAsync(tenant);
return Ok(result);
}
Couple of things I noticed.
You're trying to do a POST request with a JSON body. On the client, your request looks fine.
As I understand the POST body is
{ clientid: 'some-client-id' }
The interesting thing is in the web API you receive it as
public async Task<IHttpActionResult> SetTenantActive([FromBody]string clientid)
This is possibly the culprit. Your API is expecting a string as a POST body where it is a json object. Have you tried changing the type to dynamic or JObject?
So, essentially,
public async Task<IHttpActionResult> SetTenantActive([FromBody]dynamic clientRequest)
OR
public async Task<IHttpActionResult> SetTenantActive([FromBody]JObject clientRequest)
Alternately,
If you want to continue using your API as is, then you can just change the request you’re making from the client to ’some-client-id’ instead of { clientid: 'some-client-id' }
Change
const options = {
method: 'post',
body: JSON.stringify({ clientid : selectedRows[0].key.toString() }) ,
config: {
headers: {
'Content-Type': 'application/json'
}
}
};
to
const options = {
method: 'post',
body: JSON.stringify({ clientid : selectedRows[0].key.toString() }) ,
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
};
Check your server settings. By default it should support json but its better to verify it. Also try to clear Accept header in yor api code and set to * which means all types.
Moreover check adalApiFetch method. What headers it send? Is the format of Content-Type used & set correctly?
For a simple RESTFul call like that you could follow suggestion naming conventions along with HTTP verbs that better clarifies the intention and simplify the call itself. No need to over complicate the API model for such a simple call.
Something like
[HttpPut] // Or HttpPost. PUT is usually used to update the resourcce
[Route("api/Tenant/{clientid}/Active")]
public async Task<IHttpActionResult> SetTenantActive(string clientid) {
var tenantStore = CosmosStoreFactory.CreateForEntity<Tenant>();
var allTenants = await tenantStore.Query().Where(x => x.TenantDomainUrl != null).ToListAsync();
var updates = new List<Task>();
foreach(Tenant ten in allTenants) {
ten.Active = false;
updates.Add(tenantStore.UpdateAsync(ten));
}
await Task.WhenAll(updates);
var tenant = await tenantStore.Query().FirstOrDefaultAsync(x => x.clientid == clientid);
if (tenant == null)
{
return NotFound();
}
tenant.Active = true;
var result = await tenantStore.UpdateAsync(tenant);
return Ok(result);
}
And on the client
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
if(selectedRows[0].key != undefined){
var clientid = selectedRows[0].key;
console.log(clientid);
var url = "/Tenant/" + clientid + "/Active"
const options = {
method: 'put'
};
adalApiFetch(fetch, url, options)
.then(response => {
if(response.status === 200){
Notification(
'success',
'Tenant set to active',
''
);
}else{
throw "error";
}
})
.catch(error => {
Notification(
'error',
'Tenant not activated',
error
);
console.error(error);
});
}
},
getCheckboxProps: record => ({
type: Radio
}),
};
Why are you using post? From a 'REST`y point of view, it is used to create an entity (a tenant in your case).
The simple request intended can be solved via GET with the clientid as part of the route:
[HttpGet]
[Route("api/Tenant/SetTenantActive/{clientid}")]
public async Task<IHttpActionResult> SetTenantActive(string clientid)
{
// ...
}

Categories

Resources