Im trying to update my states after a form submit , without refreshing the page.
part of my ReactJs code :
export default class DashboardRA extends Component {
constructor(props)
{
super(props);
this.state = {
json:JSON.parse(props.data),//data received from a laravel controller used to implement the page.
pdf: '',
...
...
async onSubmitButton(e) {
e.preventDefault();
const formData = new FormData();
formData.append("pdf", this.state.pdf);
...
const response = await axios.post("/uploadFile", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(function (response) {
// console.log(response.data); //data from the DB
this.setState({
json:JSON.parse(response.data)
});
})
.catch(err => {
console.log(err);
this.setState({errorMessage: err.message});
})
}
but it shows the following error : Cannot read property 'setState' of undefined
Just change your code to
.then((response) => {
// console.log(response.data); //data from the DB
this.setState({
json:JSON.parse(response.data)
});
})
You're gwtting the error because when you refer to this in function you refer to whatever context this function is called in (some Promise internals). By using arrow syntax you're automatically binding it to the context it's declared in (component in your case)
It will be because your trying to acces to a method which is no defined in your response function. You have to assign this to variable before running axios. please check this out
export default class DashboardRA extends Component {
constructor(props)
{
super(props);
this.state = {
json:JSON.parse(props.data),//data received from a laravel controller used to implement the page.
pdf: '',
...
...
async onSubmitButton(e) {
e.preventDefault();
const formData = new FormData();
formData.append("pdf", this.state.pdf);
...
const my_this = this;
const response = await axios.post("/uploadFile", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(function (response) {
// console.log(response.data); //data from the DB
my_this.setState({
json:JSON.parse(response.data)
});
})
.catch(err => {
console.log(err);
my_this.setState({errorMessage: err.message});
})
}
Related
In refactoring my Bug Report code I moved a function into a new Class 'Logger', and call the static method as seen below:
$("#bugForm").submit((e) => {
e.preventDefault()
const input = document.getElementById('nameInput');
bugInfo = {
"name": `[${ticket.id}] Bug report`,
"story_type" : "Bug",
"description": `+ ${urlHelper.zendeskTicketUrl}` + " \n" + `+ ${input.value}`,
}
Logger.logInfo(bugInfo).then(collapse.collapse('toggle'))
})
});
however when I run the static method I receive the following error:
Uncaught (in promise) ReferenceError: metadata is not defined
Logger.js
class Logger {
constructor(settings) {
this.settings = settings;
}
static async logInfo(data = {}) {
console.log('Hello!')
const url = 'exampleUrl'
const response = fetch(url, {
method: 'POST',
headers: {
"Token": `${metadata.settings.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
return response.json();
}
}
In an attempt to fix this, I placed the following line in my code:
const logger = new Logger(metadata.settings);
And received the following error:
Uncaught (in promise) ReferenceError: Cannot access 'Logger' before initialization
I originally only made the class to use its static method, does the need for metadata prevent me from doing this? Am I not using this correctly?
So the problem the way you're passing the metadata.setting
I have changed way you use setting. Here's a working snippet
$("#bugForm").submit((e) => {
e.preventDefault()
const input = document.getElementById('nameInput');
// logic here
const bugInfo = {
info: "Hello"
}
// changed here as I removed static
logger.logInfo(bugInfo).then(console.log('print'))
});
class Logger {
constructor(settings) {
// getting the settings here and assigning it to the constructor variable
this.settings = settings;
console.log('hello', this.settings)
}
// removed static
async logInfo(data = {}) {
console.log('Hello!')
const url = 'exampleUrl'
console.log(data);
console.log(this.settings)
const response = fetch(url, {
method: 'POST',
headers: {
// using it here while calling the method
"Token": `${this.settings.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
return response.json();
}
}
const metadata = {
settings: {
token: 'hello'
}
}
const logger = new Logger(metadata.settings);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="bugForm">
<button type="submit">
Submit
</button>
</form>
I hope you can help me.
I'm trying to get a response from an API and use that information in another file.
I have 3 files:
api.jsx
import axios from 'axios';
export const api = (url, data) => {
const { path, method } = url;
let result ={};
axios({
method: method,
url: path,
data: data
})
.then(res => {
result = res.data;
console.log(res.data);
})
.catch(err => console.error(err));
return result;
};
url.jsx
export const URL = {
users:
{
getAllUsers: { path:'/users', method: 'post'},
login: { path:'/login', method: 'post'},
register: { path:'/register', method: 'post'},
version: { path:'/', method: 'get'},
}
}
app.js (within the render)
const data = {
email: 'hello#world.com',
password: '12345',
};
let result = api(URL.users.login, data);
console.log(result);
In the api file i get the proper response but in the react component no. I am aware that It's a problem of sync as i get first the console of app.jsx and later on the console of the api.jsx but i would like to respect the current structure or make something similar.
Any idea how to fix this without many changes?
PS. sorry about the mess. I tried to highlight all the code but for some reason it is not working fine.
You want to return a Promise in api.jsx
api.jsx
export const api = (url, data) => {
const { path, method } = url
return axios({ // the axios call returns a promise because of its .then; you can just return it
method: method,
url: path,
data: data
})
.then(res => {
return res.data;
})
.catch(err => {
console.error(err)
})
}
there are two independent components.A button and a form.
I can "$store.dispatch" an action to vuex by pressing down the button
addWorkerSubmit: async function () {
...
await this.$store.dispatch('workermanage/addWorkerSubmit', formData)
}
in vuex ,there a function which can post a backend-function to add a data into database
const actions = {
...
async addWorkerSubmit ({ commit }, formData) {
let { status, data: { code, msg } } = await axios.post(`/manager/worker_manage/addStaff`, formData, {
headers: { 'content-type': 'multipart/form-data' }
})
if (status === 200 & code === 0) {
...
}
}
}
but while the new data insert in to database, the form component can not reload this newdata. only refresh the web page, the new data can add into the table
<Table
border
height="700"
:columns="peopleColumns"
:data="persons"
>
...
</Table>
...mapState({ persons: state => state.workermanage.staff.staff })
I checked there Only the original data but no newly added data in "state.workermanage.staff.staff" before refresh web page
The data which in "state.workermanage.staff.staff" were taken by "nuxtServerInit" function from database
actions: {
async nuxtServerInit ({ commit }, { req, app }) {
let { status, data: { code, result } } = await app.$axios.get('/manager/worker_manage/getStaff')
commit('workermanage/setStaff'...
}
what should I do can make the data in table and "state.workermanage.staff.staff" real-time updates,thanks
Commit a mutation "workermanage/addStaff" in action addWorkerSubmit. Can backend return added staff? If so:
const actions = {
...
async addWorkerSubmit ({ commit }, formData) {
let { status, data: { code, msg, staff } } = await axios.post(`/manager/worker_manage/addStaff`, formData, {
headers: { 'content-type': 'multipart/form-data' }
})
if (status === 200 & code === 0) {
commit('workermanage/addStaff', staff)
}
}
}
const mutations = {
addStaff(state, payload) {
state.staffs.push(payload)
}
}
If backend dont return added staff. You can query updated list (same action as nuxtServerInit) or get added staff from formData
I am trying to call an express endpoint with fetch from my react app and pass the body as a JSON object so that I can include more data within it. The call currently works when I set body directly to token.id, but I want to include more data. For now I'm just constructing the JSON with that one piece of data, but cannot seem to get the server to properly handle it. I have the working lines of code commented out, which I know work as I am seeing the calls make it through to the stripe API. With the new code I get as far as the token creation working, but the create customer call does not show up on the stripe API.
import React, {Component} from 'react';
import {CardElement, injectStripe} from 'react-stripe-elements';
class CheckoutForm extends Component {
constructor(props) {
super(props);
this.state = {complete: false};
this.submit = this.submit.bind(this);
}
async submit(ev) {
let {token} = await this.props.stripe.createToken({name: "Name"});
let data ={
id: token.id
}
let response = await fetch("/charge", {
method: "POST",
//Works with the commented code
//headers: {"Content-Type": "text/plain"},
//body: token.id,
headers: {"Content-Type": "application/json"},
body: JSON.stringify(data)
}).then(response => response.json());
if (response.ok) console.log("Purchase Complete!")
if (response.ok) this.setState({complete: true});
}
render() {
if (this.state.complete) return <h1>Purchase Complete</h1>;
return (
<div className="checkout">
<p>Would you like to complete the purchase?</p>
<CardElement />
<button onClick={this.submit}>Send</button>
</div>
);
}
}
export default injectStripe(CheckoutForm);
This is my server code:
const app = require("express")();
const stripe = require("stripe")("sk_test_oE2EqjsM7mWqgRRwaomptrdX");
app.use(require("body-parser").text());
app.post("/charge", jsonParser, async (req, res) => {
try {
var userdata = JSON.parse(req.body);
let {status} = stripe.customers.create({
description: "Test Person",
//Commented code currently works
//source: req.body
//Below is how I expected this to work when passing the body as JSON
source: userdata.id
});
res.json({status});
} catch (err) {
res.status(500).end();
}
});
app.listen(9000, () => console.log("Listening on port 9000"));
I have banged around in this for a while now. Looked at this question but its not quite what I want.
In a nutshell... I have const expression = to a function that is chained to another function that makes an API call in a separate file to LoginContainer but in the same folder -(its called reducer.js but has the actions as well at this stage). If successful it receives a token which it saves in local storage. this works fine.
Here it is.
import { fetch, addTask } from 'domain-task'
import { saveJwt, clearJwt } from '../auth/jwt'
import { handleErrors } from '../utils/http'
const REQUEST_LOGIN_TOKEN = 'REQUEST_LOGIN_TOKEN'
const RECEIVE_LOGIN_TOKEN = 'RECEIVE_LOGIN_TOKEN'
const ERROR_LOGIN_TOKEN = 'ERROR_LOGIN_TOKEN'
const REQUEST_USER = 'REQUEST_USER'
const RECEIVE_USER = 'RECEIVE_USER'
const ERROR_USER = 'ERROR_USER'
// ******************* action
export const requestLoginToken = (username, password) =>
(dispatch, getState) => {
dispatch({ type: REQUEST_LOGIN_TOKEN, payload: username })
const payload = {
userName: username,
password: password,
}
const task = fetch('/api/jwt', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_LOGIN_TOKEN, payload: data })
saveJwt(data)
})
.catch(error => {
clearJwt()
dispatch({ type: ERROR_LOGIN_TOKEN, payload: error.message })
})
addTask(task)
return task
}
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import Login from './Login'
import { requestLoginToken } from './reducer'
class LoginContainer extends Component {
static contextTypes = {
router: PropTypes.object.isRequired
}
componentWillReceiveProps(nextProps) {
if (nextProps.isAuthorised) {
this.context.router.push('/')
}
}
submit = (values) => {
console.log('got values!', values)
this.props.requestLoginToken(values.username, values.password)
}
render() {
return (
<Login onSubmit={this.submit} />
)
}
}
const mapStateToProps = (state) => ({
isAuthorised: state.login.isAuthorised,
})
const mapDispatchToProps = (dispatch) => ({
requestLoginToken: (username, password) => dispatch(requestLoginToken(username, password)),
//requestSelectData: (values = {}) => dispatch(requestSelectData(values = {})),
})
export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer)
In the loginContainer (above), Once the "userName" and "password" have been entered and the submit button clicked, the expression "requestLoginToken" is called.
My Problem
I want to fetch a significant amount of data based on the above expression "requestLoginToken" successfully saving a JWT token into local storage. It does this successfully now with the right username and password.
I know I can't make another call from within the expression "requestLoginToken" using a ".then" as it specifically needs to retrieve and then save a token first - I have to wait till it finishes to know if I have a token. I need to run a second expression that only gets run if this promise is successful ie via a conditional statement. "If (JWT) etc"
1) Could someone tell me where and how I add this conditional statement. Im thinking its in the Logincontainer in the submit? ..how do would I structure the condition?
2) Where and how do I add the const = function for the retrieval of the data eg if I place it in another separate file do I still or even need to register it in mapDispatchToProps in the loginContainer etc
EDIT
Taking Nate Kimball's answer and running with it. Decided to split it out into its own "const" called "selectData" which I plan to call right underneath the line "saveJwt(data)".
However I find I am now getting an error:
Unexpected Token , expected
Its on the very last line of the following code block below.. (right curly bracket has red under it) checked it for sytax but cant workout why.
I think the approach is correct though.
const selectData = () => {
dispatch({ type: REQUEST_SELECT_DATA })
const token = jwt.access_token
const headers = new Headers({
'Authorization': `Bearer ${token}`
})
const selectData = fetch('/api/SelectData/SelectData', {
method: 'GET',
headers,
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_SELECT_DATA, payload: data })
.catch(error => {
clearJwt()
dispatch({ type: ERROR_SELECT_DATA, payload: error.message })
})
}
}
I don't see any reason why you couldn't nest a second fetch from within your action after a successful call:
export const requestLoginToken = (username, password) =>
(dispatch, getState) => {
dispatch({ type: REQUEST_LOGIN_TOKEN, payload: username })
const payload = {
userName: username,
password: password,
}
const task = fetch('/api/jwt', {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
})
.then(handleErrors)
.then(response => response.json())
.then(data => {
dispatch({ type: RECEIVE_LOGIN_TOKEN, payload: data })
saveJwt(data)
// Since this is where you receive your login token,
// You can dispatch an action to acknowledge you're fetching:
dispatch({ type: SECOND_DATA_FETCHING })
// This is also where you would make your next call:
fetch('/api/etc', { ...config })
.then(response => {
// You can use your reducer to both inform a successful call &
// store the received data
dispatch({ type: SECOND_DATA_SUCCESS, payload: response.data })
})
.catch(error => {
// Let your app know the call was unsuccessful:
dispatch({ type: SECOND_DATA_FAILED, payload: error.message })
})
// Note: if you don't like the nested ugliness, you could optionally
// put this entire nested fetch chain into a separate action and just
// dispatch that when you get your token.
})
.catch(error => {
clearJwt()
dispatch({ type: ERROR_LOGIN_TOKEN, payload: error.message })
})
addTask(task)
return task
}
At that point, all you need to do is update your mapStateToProps function in your component to receive the data and/or the status of that second layer of fetched data:
// Make sure you have a default status for that second data
// just in case your token call fails.
const mapStateToProps = (state) => ({
isAuthorised: state.login.isAuthorised,
secondData: state.login.secondData,
secondDataStatus: state.login.secondDataStatus
})
You can use requestLoginToken in another action creator:
function loginAndFetch() {
return function(dispatch, getState) {
dispatch(requestLoginToken()).then(token => {
return fetch(...) // use token here
})
}
}
As an alternative, you could save the token you got to the store, then have another component listen to changes to the token and dispatch another action when the token changes.
class Container extends Component {
componentDidUpdate(prevProps, prevState) {
if (this.props.token != prevProps.token) {
dispatch(fetchSignificantAmountOfData())
}
}
}
Container is connected and maps the stored token into props.token
You can write a custom middleware to solve this problem like this:
https://github.com/erikras/react-redux-universal-hot-example/blob/master/src/redux/middleware/clientMiddleware.js
And then, you can use the action like this:
export function myAction() {
return {
types: [LOAD, SUCESS, FAIL],
promise: (client) => client.get('/some_api')
};
}
The middleware will dispatch the LOAD reducer first, then if promise is resolve, it call SUCESS; Otherwise, FAIL is called.