I am using Clarifai API face detection and it is unable to fetch the URL which is provided from the constructor, rather than a variable which Clarifai provides in the default code
class App extends Component{
constructor(){
super();
this.state = {
input : '',
IMAGE_URL: '',
}
}
onInputChange = (event) =>{
this.setState({input: event.target.value});
}
onSubmit = () =>{
this.setState({IMAGE_URL : this.state.input});
const raw = JSON.stringify({
"user_app_id": {
"user_id": USER_ID,
"app_id": APP_ID
},
"inputs": [
{
"data": {
"image": {
"url": this.state.IMAGE_URL
}
}
}
]
});
const requestOptions = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Key ' + PAT
},
body: raw
};
// NOTE: MODEL_VERSION_ID is optional, you can also call prediction with the MODEL_ID only
// https://api.clarifai.com/v2/models/{YOUR_MODEL_ID}/outputs
// this will default to the latest version_id
fetch("https://api.clarifai.com/v2/models/" + MODEL_ID + "/versions/" + MODEL_VERSION_ID + "/outputs", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
I started facing this issue when I added IMAGE_URL in constructor to update it from my input form on the webpage.
It works fine if i move IMAGE_URL out from the constructor and making it a variable and hard code the image url in the editor
Edit:
This is the code after some tweaks. Still same error
onInputChange = (event) =>{
this.setState({input: event.target.value});
console.log(typeof(input),'TYPE OF INPUT');
var inp = this.state.input;
return inp
//console.log(inp);
console.log(typeof(inp)); //it is string here
}
onSubmit = () =>{
this.setState({IMAGE_URL : this.state.inp});
const raw = JSON.stringify({
"user_app_id": {
"user_id": USER_ID,
"app_id": APP_ID
},
"inputs": [
{
"data": {
"image": {
"url": this.state.IMAGE_URL
}
}
}
]
Edit 2:
It's working now and I guess I broke some rules. I have declared a global variable and passed the value of the input field to it and then used it in my API.
var inp = ''; //THIS IS THE NEW VARIABLE
class App extends Component{
constructor(){
super();
this.state = {
input : '',
IMAGE_URL: '',
}
}
onInputChange = (event) =>{
this.setState({input: event.target.value});
inp = event.target.value;
console.log(inp);
return inp;
}
onSubmit = () =>{
console.log('*********',inp,'***********');
this.setState({IMAGE_URL : this.state.input});
const raw = JSON.stringify({
"user_app_id": {
"user_id": USER_ID,
"app_id": APP_ID
},
"inputs": [
{
"data": {
"image": {
"url": inp
}
}
}
]
Seems like you've found a workaround with a global variable; but I think the actual problem was with:
this.setState({IMAGE_URL : this.state.input});
in the onSubmit function.
The setState function in react:
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass.
https://reactjs.org/docs/react-component.html#setstate
So basically you're making a request to update that variable (IMAGE_URL) and then immediately using it. Since React is trying to optimize writes, you are trying to use it before it is actually updated. You can request that update and then use the already populated version if you want.
I think the following code should work and allow you to keep out of the global scope (if that bothers you).
class App extends Component{
constructor(){
super();
this.state = {
input : '',
IMAGE_URL: '',
}
}
onInputChange = (event) =>{
this.setState({input: event.target.value});
}
onSubmit = () =>{
// This may not trigger right away, but if you want to transfer the this.state.input to this.state.IMAGE_URL it _eventually_ will
this.setState({IMAGE_URL : this.state.input});
const raw = JSON.stringify({
"user_app_id": {
"user_id": USER_ID,
"app_id": APP_ID
},
"inputs": [
{
"data": {
"image": {
"url": this.state.input
}
}
}
]
});
const requestOptions = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Authorization': 'Key ' + PAT
},
body: raw
};
// NOTE: MODEL_VERSION_ID is optional, you can also call prediction with the MODEL_ID only
// https://api.clarifai.com/v2/models/{YOUR_MODEL_ID}/outputs
// this will default to the latest version_id
fetch("https://api.clarifai.com/v2/models/" + MODEL_ID + "/versions/" + MODEL_VERSION_ID + "/outputs", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
Related
I want to try and use react to fetch data efficiently using useEffect appropriately.
Currently, data fetching is happening constantly, instead of just once as is needed, and changing when there is a input to the date period (calling different data).
The component is like this,
export default function Analytics() {
const {
sentimentData,
expressionsData,
overall,
handleChange,
startDate,
endDate,
sentimentStatistical,
} = useAnalytics();
return (
UseAnalytics is another component specifically for fetching data, basically just a series of fetches.
i.e.,
export default function useAnalytics() {
....
const { data: sentimentData } = useSWR(
`dashboard/sentiment/get-sentiment-timefilter?startTime=${startDate}&endTime=${endDate}`,
fetchSentiment
);
....
return {
sentimentData,
expressionsData,
overall,
handleChange,
setDateRange,
sentimentStatistical,
startDate,
endDate,
};
}
Thanks in advance,
The apirequest is like this,
export async function apiRequest(path, method = "GET", data) {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
//this is a workaround due to the backend responses not being built for this util.
if (path == "dashboard/get-settings") {
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.error === "error") {
throw new CustomError(response.code, response.messages);
} else {
return response;
}
});
}
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.status === "error") {
// Automatically signout user if accessToken is no longer valid
if (response.code === "auth/invalid-user-token") {
firebase.auth().signOut();
}
throw new CustomError(response.code, response.message);
} else {
return response.data;
}
});
}
I think using useEffect here is the right approach. i.e.,
useEffect(()=>{
// this callback function gets called when there is some change in the
// state variable (present in the dependency array)
},[state variable])
I'm confused about how to update the constants properly, something like this seems like one approach, but not sure about how I can use useEffect to update these variables properly, or if I should be doing this inside of useAnalytics?
i.e.,
const [analytics, setAnalytics] = useState({
sentimentData: {},
expressionsData: {},
overall: {},
handleChange: () => {},
startDate: '',
endDate: '',
sentimentStatistical:{},
});
useEffect(()=>{
// this callback function gets called when there is some change in the
// state variable (present in the dependency array)
},[state variable])
const {
sentimentData,
expressionsData,
overall,
handleChange,
startDate,
endDate,
sentimentStatistical,
} = useAnalytics();
Realised SWR is a hook, need to use SWR documentation :P
You have to store the requested information in states inside your custom hook. Then you could consume this hook wherever you want. This should work.
Define custom hook
const useAnalitycs = () => {
const [analytics, setAnalytics] = useState({
sentimentData: {},
expressionsData: {},
overall: {},
startDate: '',
endDate: '',
sentimentStatistical:{},
});
const handleChange = () => {
/* */
};
useEffect(() => {
const fetchData = async () => {
// const response = await apiCall();
// setAnalytics(...)
};
fetchData();
}, []); // called once
return {
...analytics,
handleChange
};
};
Consume useAnalytics hook
const ComponentConsumerA = () => {
/*
const { state/methods you need } = useAnalytics()
...
*/
};
const ComponentConsumerB = () => {
/*
const { state/methods you need } = useAnalytics()
...
*/
};
I have been learning react for a while and have been working on creating a pet project. My friend created a test case which tests out some notification message from a method. This method in turn will use a constant from another class.
Below notification component utilizes a set of props(especially the partner props) passed over from routes.js.
class Notification extends Component {
constructor(props) {
super(props);
this.state = {
orientation: "ltr",
services: {
"applications": [],
"eta": "",
"start": ""
},
statuses: {},
locale_date: new Date(),
modal: {
open: false,
header: null,
desription: null
},
// This shouldn't be hardcoded but there are issues with passing this in as a prop in Routes.js
partner: props.partner
}
this.refreshEndpoints();
}
refreshEndpoints = () => {
const ref = this;
axios
.get(this.state.partner.get_device_status_url)
.then(response => {
var statuses = response.data;
if((typeof statuses) !== 'object') return false;
ref.setState({
statuses: statuses
});
}).catch(error => {
});
}
handleCreateNotification = () => {
const ref = this;
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(ref.state.services)
};
adalApiFetch(fetch, this.state.partner.get_endpoint_notifications_banner, options)
.then(response => {
ref.setState({
modal: {
open: true,
header: "Success",
description: "Successfully Created Notification"
}
});
})
.catch(function (error) {
ref.setState({
modal: {
open: true,
header: "Error",
description: "Failed to Create Notification"
}
});
});
}
handleDeleteNotification = () => {
const ref = this;
const options = {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(ref.state.services)
};
adalApiFetch(fetch, this.state.partner.get_endpoint_notifications_banner, options)
.then(response => {
ref.setState({
modal: {
open: true,
header: "Success",
description: "Successfully Deleted Notification"
}
});
})
.catch(function (error) {
ref.setState({
modal: {
open: true,
header: "Error",
description: "Failed to Delete Notification"
}
});
});
}
In routes.js I have route for calling out the above component which passes the props for partner.
<ProtectedNotificationPage orientation={orientation} partner={PartnerOne}/>
ParnerOne.js:
export const get_endpoint_notifications_banner = "<some url>"
export const get_device_status_url = "<some url>"
<class components>
I want to utilize the above const in notification component. And I was able to accomplish that using props.partner inside the state method.
But below test case is failing due to undefined property which is strange. But the notification functionality completely works fine. clearing and adding notification has no issues.
describe('Notification component', () => {
it('handleCreateNotification - Success', async () => {
const wrapper = shallow(<Notification />);
await wrapper.instance().handleCreateNotification();
expect(wrapper.state().modal).toEqual(
{
open: true,
header: "Success",
description: "Successfully Created Notification"
}
);
});
it('handleDeleteNotification', async () => {
const wrapper = shallow(<Notification />);
await wrapper.instance().handleDeleteNotification();
expect(wrapper.state().modal).toEqual(
{
open: true,
header: "Success",
description: "Successfully Deleted Notification"
}
);
});
I apologize for my lack of knowledge.. But this is something I couldn't figure out over tutorials/blogs. And I really appreciate if anyone able to point out the issue or reference for fixing this.
I tried utilizing bind across methods, which is something I thought might fix. But didn't workout. Apart from that I also tried accessing the props directly
like this.props.partner.get_device_status_url.. And still test case were failing.
I would suggest the following:
Importing into Notification.js:
const { get_endpoint_notifications_banner, get_device_status_url } = '<path_to_file>'.
You can now access these variables directly inside Notification.js.
Test case was having some issue. When I passed the partner one as props to my test case. It fixed the issue. It was looking for missing props
I am currently developing an app that requires login/logout. I am using google-login-react (Google OAuth2) to handle most of the signing in heavy work. From there, I am posting the google access token along with some other stuff (id, email, last login, etc..) to my RESTful API. After the user is authenticated, they are redirected to a dashboard. From there, the user can sign out of the application.
What I am having difficulty with is UPDATING the user and the user object after the user signs back in. Right now, every time the user logs back in, it is posting a new object (and thus a new user) to my API. I'm looking to simply just show the updated LAST LOGIN and to store the existing user in the ID they had already been assigned upon initial log in.
Right now, this is what I have thus far:
PostData.js
export function PostData(type, userData) {
let BaseURL = 'https://app.someApp.com/';
return new Promise((resolve, reject) =>{
fetch(BaseURL+type, {
headers:{
"Accept": "application/json",
"Content-Type": "application/json"},
'Access-Control-Allow-Origin':'*',
'Content-Security-Policy':'upgrade-insecure-requests',
method: 'POST',
mode: 'cors',
body: JSON.stringify(userData)
})
.then((response) => response.json())
.then((res) => {
resolve(res);
})
.catch((error) => {
reject(error);
});
});
}
Login.js
class Login extends Component {
constructor(props) {
super(props);
this.state = {
loginError: false,
redirect: false
};
this.signup = this.signup.bind(this);
}
signup(res, type) {
let postData;
var currentTime = new Date();
if (type === 'google' && res.profileObj.email) {
postData = {
email: res.profileObj.email,
realname: res.profileObj.name,
googleId: res.profileObj.googleId,
googleAccessToken: res.Zi.access_token,
googleImageURL: res.profileObj.imageUrl,
lastLogin: currentTime
};
}
if (postData) {
PostData('api/v1/appUsers', postData).then((result) => {
let responseJson = result;
localStorage.setItem("userData", JSON.stringify(responseJson));
this.setState({redirect: true});
});
} else {}
};
render() {
if (this.state.redirect || localStorage.getItem('userData')) {
return (<Redirect to={'/'}/>)
}
const responseGoogle = (response) => {
let isSignedIn = true;
console.log("google console");
console.log(response);
console.log(isSignedIn);
this.signup(response, 'google');
return isSignedIn;
}
Home.js
signOut(e) {
e.preventDefault()
if (localStorage.getItem('userData')) {
localStorage.removeItem('userData');
this.props.history.push('/login')
console.log('shit works');
}
}
constructor(props){
super(props);
this.state = {
name:'',
redirect: false,
};
}
componentDidMount() {
let data = JSON.parse(localStorage.getItem('userData'));
console.log(data);
}
render() {
if(!localStorage.getItem('userData') || this.state.redirect){
return (<Redirect to={'/login'}/>)
}
**I'm mainly looking for a syntax solution as opposed to the logic solution as I am aware of the logic behind this
I'm posting data from form to my json-server url localhost:3000/recipes and I'm trying to get data in other component without refreshing the page. I'm posting some data to recipes url and when i go back to other hashURL on page I need to refresh my page to get result. Is there any way to get data async from life cycles or something similar ?
componentDidMount() {
recipesService.then(data => {
this.setState({
recipes: data
});
});
}
recipe.service
const url = "http://localhost:3000/recipes";
let recipesService = fetch(url).then(resp => resp.json());
let sendRecipe = obj => {
fetch(url, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(obj)
})
.then(resp => resp.json())
.then(data => console.log(data))
.catch(err => console.log(err));
};
module.exports = {
recipesService,
sendRecipe
};
Probably you want to use something like Redux. :)
Or you can create your cache for this component:
// cache.js
let value;
export default {
set(v) { value = v; },
restore() { return value; },
};
// Component.js
import cache from './cache';
...
async componentDidMount() {
let recipes = cache.restore();
if (!recipes) {
recipes = await recipesService;
cache.set(recipes);
}
this.setState({ recipes });
}
***This component makes a REST API call and parses promise value and renders the data in the form of a table.
load(function) makes the API call and takes orderType as input. OrderType is passed as a query parameter from the navigation component which is not included here.
class SampleController extends React.Component {
constructor(props){
super(props);
let orderType = this.props.location.query.orderType;
this.load = this.load.bind(this);
this.load(orderType);
this.state = {orderType: orderType, data: null}
}
load(orderType) {
let self = this;
console.log("order type is" + orderType);
let baseURL = base_urls.orderMetricsBaseURL;
console.log("base URL is", baseURL);
let url = baseURL + "/getData";
let response_data = fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
order_type: orderType
})
})
.then((response) => response.json())
.then((responseJson) => {
let order_data = responseJson;
console.log("responseeeeee is: ", order_data);
self.setState({data: order_data});
self.forceUpdate();
})
.catch((error) => {
console.error(error);
});
}
render() {
var content = <DataTable dataList = {this.state.data} />;
return (
content
);
}
}
export { SampleController as default };
remove this line this.load(orderType); from constructor and put it inside componentDidMount:
constructor(){
//....
}
componentDidMount() {
const orderType = this.props.location.query.orderType;
this.load(orderType);
}
You need to do setState to trigger a rerun/rerender on your component.
From the documentation:
setState() does not update the state immediately, so there's a chance you are losing that update because of the forceUpdate(), which shouldn't be needed as setState() will trigger a re-render by itself. Try removing the forceUpdate() call.
Update:
In case you want to call your load() method each time your orderType you'd need to subscribe to the componentWillReceiveProps method.
Example:
class SampleController extends React.Component {
constructor(props){
super(props);
let orderType = this.props.location.query.orderType;
this.load = this.load.bind(this);
this.load(orderType);
this.state = {orderType: orderType, data: null}
}
componentWillReceiveProps(nextProps) {
const orderType = nextProps.location.query.orderType;
const prevOrderType = this.props.location.query.orderType;
if (prevOrderType !== orderType) {
this.load(orderType);
}
}
load(orderType) {
let self = this;
console.log("order type is" + orderType);
let baseURL = base_urls.orderMetricsBaseURL;
console.log("base URL is", baseURL);
let url = baseURL + "/getData";
let response_data = fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
order_type: orderType
})
})
.then((response) => response.json())
.then((responseJson) => {
let order_data = responseJson;
console.log("responseeeeee is: ", order_data);
self.setState({data: order_data});
})
.catch((error) => {
console.error(error);
});
}
render() {
var content = <DataTable dataList = {this.state.data} />;
return (
content
);
}
}
export { SampleController as default };