map axios response then use it for another response - reactjs

I want to map an array of images, but because Directus is identifying an image or a file as an id in the collection's item.
right now I need to map the array of id from the response in the first request then using that id for making another get request in another endpoint which is right here is files.
the database that I use is DirectusCms, and the front end is react.js
class ComponentToPrint extends React.Component {
constructor(props) {
super(props);
this.handleLoad = this.handleLoad.bind(this);
this.state = {
nama: [],
kk:[],
ijazah:[]
};
}
componentDidMount() {
window.addEventListener('load', this.handleLoad);
}
componentWillUnmount() {
window.removeEventListener('load', this.handleLoad)
}
handleLoad() {
let nama_1;
let id_kk;
const url = `https://sensor/public/gemaclc/items/pendaftar?access_token=sensor`
const url2 = `https://sensor/public/gemaclc/files/${id_kk}?access_token=sensor`
axios(url, {
method: 'get',
headers:{ 'Content-Type': 'application/json'} })
.then(res => {
const nama = res.data.data;
const nama = res.data.data;
console.log( nama)
this.setState( {nama} );
})
.catch(error => console.error('Error:', error))
// .then(res => {console.log('Success:', nama)})
// this.setState( nama );
// console.log(nama)
axios(url2,{
method:`get`,
headers:{ 'Content-Type': 'application/json'} })
.then(res => {
id_kk = res.data.data;
console.log( id_kk)
// this.setState( {id_kk} );
})
}
render() {
return (
<>
{this.state.nama.map(node =>
<div key={node.id}>
<h2>Nama :{node.nama}</h2>
<h2>Tanggal lahir : {node.tanggal_lahir}</h2>
<h2>Tempat lahir : {node.tempat_lahir}</h2>
<h2>Email : {node.email}</h2>
<h2>Email : {node.telepon}</h2>
<h2>kk : {node.kartu_keluarga}</h2>
</div>
)}
</>
);
}
}

When requesting from the items endpoint in Directus one can supply a query parameter called fields, with this you can get nested information from relations, including the hash etc, see: https://docs.directus.io/api/items.html#fields-optional
If you have the private hash, you can use the assets endpoint to get the actual image, see: https://docs.directus.io/api/assets.html#get-an-asset
PS: You haven't mentioned what your problem is, so I guessed it based on my overall knowledge of the topic - next time also mention your exact problem, what you've tried and what didn't work etc

Related

React - Trying to update Fetch call in Parent Component based off of User Input in Child Component

So I am still very much a beginner when it comes to React. I am trying to build an application where the user inputs their location, which would then dynamically update the URL within my fetch call. Based off the results of the fist fetch call, I would then dynamically update a second fetch call, to a different API, providing me with the information needed.
As it stands right now, both Fetch calls are properly working, when provided the right information. The problem I am currently running into is, I don't believe my parent component is re-rendering with the update information. I am trying to console log one of my states but it keeps coming back as blank. The weird part is, the other state that is being created within the child component, is coming back with the right information. Any help would be greatly appreciated!
APP.JS
import Home from './Home/Home';
import Userinput from './UserInput/Userinput';
const url =
'https://api.openuv.io/api/v1/uv?lat=-33.34&lng=115.342&dt=2018-01-24T10:50:52.283Z';
const url1 = `http://www.mapquestapi.com/geocoding/v1/address?key=${process.env.REACT_APP_MAP_API_KEY}`;
class App extends Component {
constructor(props) {
super(props);
this.state = {
uvIndex: '',
lat: '',
long: '',
inputValue: '',
};
this.handleInputValue = this.handleInputValue.bind(this);
}
handleInputValue(val) {
this.setState({ inputValue: val });
}
componentDidMount() {
let newVal = this.state.inputValue;
fetch(`${url1}&location=${newVal}`, {
method: 'GET',
headers: {
'content-type': 'application/json',
},
})
.then((res) => res.json())
.then((res) => {
this.setState({
lat: res.results[0].locations[0].latLng.lat,
long: res.results[0].locations[0].latLng.lng,
});
// this.setState({ uvIndex: res });
})
.catch((err) => {
console.error(err);
});
fetch(url, {
method: 'GET',
headers: {
'content-type': 'application/json',
'x-access-token': `${process.env.REACT_APP_UV_API_KEY}`,
},
})
.then((res) => res.json())
.then((res) => {
console.log(res);
this.setState({ uvIndex: res });
})
.catch((err) => {
console.error(err);
});
}
render() {
console.log(this.state.lat); #this state comes back as blank
console.log(`${url1}&location=${this.state.inputValue}`); # this state comes back with the update userinput
return (
<div>
<header>
<Home />
</header>
<div>
<Userinput handleInput={this.handleInputValue} />
</div>
</div>
);
}
}
export default App;
Userinput.js
class Userinput extends Component {
constructor(props) {
super(props);
this.state = {
inputVal: '',
};
this.onInputChange = this.onInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// handle input change event
onInputChange(e) {
this.setState({ inputVal: e.target.value });
}
// handle button click event and pass data in parent
handleSubmit() {
this.props.handleInput(this.state.inputVal);
}
render() {
return (
<div>
<input value={this.state.inputVal} onChange={this.onInputChange} />
<input type='button' value='Submit' onClick={this.handleSubmit} />
</div>
);
}
}
export default Userinput;
Consider this.setState in the App Class. The first fetch() is writing your state like so:
this.setState({
lat: res.results[0].locations[0].latLng.lat,
long: res.results[0].locations[0].latLng.lng,
});
The second fetch() call sets a new state object, which removes the result from the first fetch() call, or whatever fetch() resolves faster:
.then((res) => {
console.log(res);
this.setState({ uvIndex: res });
})
You can fix this issue with object spread operator:
this.setState({...this.state, uvIndex: res });
This will keep a copy of your state, and only overwrite uvIndex (if it was set before)
Also consider the handleInputVal Method in App.js. I think same problem here. the state is overwritten with a new Object (setState doesn't update the state, it creates a new State), which means that lat and long are being set to undefined 🤔
handleInputValue(val) {
//this.setState({ inputValue: val }); sets lat and long to undefined
this.setState({...this.state, inputValue: val }); //keep lat and long values, and update inputValue with val
}

REACT Array True or False Inputs?

Im having difficulty serializing my inputs and saving it on my API. I cant manage to make it work.
Need some advice or inputs how to do this with arrays or even another simpler way.
I having problem how to put the radio button input array data into my postData that will be save to my API
postData is questionid, questiontype and answers
Questions:
1.Earth is flat? True False
2.Earth is round? True False
so my input is radio button like this
handleTF(event) {
const tfc = event.target.value;
console.log(event.target.name)
console.log(tfc);
this.setState({ [event.target.name]: tfc});
}
const tfcs = ["True", "False"]
{tfcs.map((tfc, index) => (
<label key={index}>
{tfc}
<input
name={qd.question.id}
value={tfc}
onChange={this.handleTF}
type="radio"
/>
</label>
))}
handleSubmitTF(event){
event.preventDefault();
const {
questionId,
questionType,
answer
} = this.state;
const postData = {
questionId,
questionType,
answer,
};
let sessionToken = sessionStorage.getItem('session');
let sessToken = sessionToken.replace(/\"/g, "");
fetch('sample.net', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'token'
},
body: JSON.stringify([postData]),
})
.then(response => response.json())
.then(response => {
console.log(response.title)
if(response.title === "Error"){
this.addNotification('danger', 'Danger', 'Already answered', 'top-right')
}else{
this.addNotification('success', 'Success', 'All Data is Saved', 'top-right')
this.componentDidMount();
}
})
.catch(err => {
console.log("fetch error" + err);
});
}
Context
It seems that your state is made of:
{ [event.target.name]: "True" | "False" }
But then you access it with:
const {
questionId,
questionType,
answer
} = this.state;
They do not match type / interface. In TypeScript terms your State looks like:
interface State {
[k: string]: "True" | "False";
}
Solutions
A. Send your state directly
As it has a valid structure, it would be easy to parse on the API side.
const postData = { ...this.state };
B. Send an Array of answers
Otherwise, if you need it as an Array of answers. You can transform it like:
const questions = Object.keys(this.state);
const postData = questions.map((question) => ({
question,
answer: this.state[name],
}));
Side note: Earth is flat, and it is called Discworld =o)

Github API returns another API endpoint to make another request

I'm using github's API to get a list of my repos and then filtering them to display some specified projects. The issue I'm having is some of the data is another API endpoint that needs to be requested. I've never encountered this before. Am I suppose to make another fetch request after I get the first dataset back from the API? For instance I'm currently using this specific end point
https://api.github.com/user/repos
and here is some of the data I get back
archive_url: "https://api.github.com/repos/myUserName/test-repo/{archive_format}{/ref}"
archived: false
assignees_url: "https://api.github.com/repos/myUserName/test-repo/assignees{/user}"
blobs_url: "https://api.github.com/repos/myUserName/test-repo/git/blobs{/sha}"
branches_url: "https://api.github.com/repos/myUserName/test-repo/branches{/branch}"
languages_url: "https://api.github.com/repos/alenart91/test-repo/languages"
so now if I want the languages_url data I have to make another fetch request?
here is my current code
import ProjectCard from './ProjectCard.js'
class Projects extends React.Component {
constructor(props) {
super(props);
this.state = { projects: [], name: '' }
}
componentDidMount() {
this.getProjects();
}
getProjects = () => {
const token = 'private data';
let url = 'https://api.github.com/user/repos';
fetch(url , {
mode: 'cors',
headers: {
'Accept': 'application/vnd.github.v3+json',
'Authorization': 'Bearer ' + token
}})
.then(res => {
if (res.status === 200) {
return res.json();
} else {
return res.statusText;
}
})
.then( data => {
let filteredProjects = data.filter( projectName => {
return projectName.name == 'test-repo' || projectName.name == 'test-repo';
})
this.setState( {projects: filteredProjects });
})
.catch( err => {
console.log(err);
})
}
render() {
const {projects} = this.state;
return (
<React.Fragment>
{projects.map( projects => {
return <ProjectCard language = {projects.language} description = {projects.description} url = {projects.html_url} name = {projects.name} />
})}
</React.Fragment>
);
}
}
export default Projects
Yes, you need to make another fetch request for the languages_url endpoint. Alternatively, you can use GraphQL to get the languages of a repository in a single request. See below code for the GraphQL call. Ref. GraphQL
{
repository(owner: "<<OWNER>>", name: "<<REPONAME>>") {
name
languages(first: 10) {
edges {
node {
name
}
}
}
}
}

Upload an image with reactjs and mongodb

I'm trying to make an image uploader, thanks to a form, in reactjs.
I've created an api in mongodb (thanks to express, mongoose, etc.), and i'm trying to use it in order to upload an image.
Actually, i would like to send an image file to the cloud (with Cloudinary), and get the url.
That is my form and methods :
class Header extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
data: [],
uploading: false,
image: [],
apiKey: 'xxx'
};
}
onChangeImage = e => {
this.setState({[e.target.name]: Array.from(e.target.files)});
};
sendImage = files => {
const formData = new FormData();
files.forEach((file, i) => {
formData.append(i, file)
});
fetch('http://localhost:3000/image-upload', {
method: 'POST',
headers : new Headers({
'Content-Type' : 'application/x-www-form-urlencoded',
'x-access-token' : this.state.apiKey
}),
body: formData
})
.then(res => res.json())
.then(image => {
this.setState({
uploading: false,
image
});
return true;
});
return false;
};
handleSubmit = (event) => {
event.preventDefault();
const { image } = this.state;
this.sendImage(image);
};
render() {
return(
<form onSubmit={this.handleSubmit} className="formAdd">
<input type='file' id="image" name="image" onChange={this.onChangeImage} />
<button className="contact-form-btn">
Send<i className="fa fa-long-arrow-right" aria-hidden="true"></i>
</button>
</form>
)
}
About my API Controller :
const cloudinary = require('cloudinary');
module.exports = {
create: function(req, res) {
cloudinary.config({
cloud_name: 'xxxx',
api_key: 'xxxxx',
api_secret: 'xxxxx'
});
const path = Object.values(Object.values(req.body.files)[0])[0].path;
cloudinary.uploader.upload(path)
.then(image => res.json([image]));
},
};
The error code that I get is 500 'TypeError: Cannot convert undefined or null to object'.
Indeed, it not found Object.values(Object.values(req.body.files)[0])[0].path.
What I've missed ?
Thanks.
You can use this to upload an image. Using async/await.
async uploadImage(image) {
const form = new FormData();
form.append('file', image);
form.append('upload_preset', 'g5ziunzg');
const res = await Axios.post('YOUR_CLOUDINARY_URL', form)
console.log(res)
return res.data;
}
This will return an object with the secure_url which you can store in your mongo database. I am assuming you have a backend-api for this task.
Inside your formSubmit function, you can first call this function and receive this secure_url.
Note that I am using axios here. This example can easily be translated to work with fetch.
You don't need to use Object.value since req.body.files is an array and you need to check its length before access. Try it:
const [file] = req.body.files
if (file) {
// your logic here
}

How to pass parameter in api url based on selected option?

I would like to fetch data based on selected job id.
Job id should be selected from drop down list.
Once the job id will be selected, api url should be adjusted with the property job id.
I added the select option and fetch statement. However I cannot pass the parameter in the url.
const jsonify = res => res.json();
var chart_request = new Request(
`https://xxxx.com/prod/job-id?job_id_number=${this.state.selectVal}`,
{
method: 'GET',
headers: new Headers({
'Content-Type': 'application/json'
})
}
);
const dataFetch = fetch(chart_request).then(jsonify);
export default class ZYZ extends Component {
constructor(props) {
super(props);
this.state = {
selectVal : "650"
}
}
setSelectValue = (event) => {
this.setState({
selectVal: event.target.value
});
}
render() {
return
<React.Fragment>
<select value={this.state.selectVal} onChange={this.setSelectValue}>
<option value = "650">650</option>
<option value = "1052">1052</option>
</select>
<p>{this.state.selectVal}</p>
</React.Fragment>
}
}
You can't use your state outside your class.
But if you insist, you can use componentDidMount & componentDidUpdate lifecycle methods on initial load and every select respectively, and pass the id as an argument to fetchData & chart_request as follow:
componentDidMount() {
// calling fetch, resolving the promise, and storing the data in state
fetchData(this.state.selectVal).then(data => this.setState({ data }));
}
componentDidUpdate() {
// same as above
fetchData(this.state.selectVal).then(data => this.setState({ data }));
}
The modifications for chart_request & fetchData:
const chart_request = id =>
fetch(
`https://xxxx.com/prod/job-id?job_id_number=${id}`
)
.then(response => response.json()) // instead of "jsonify"
.then(data => JSON.stringify(data))
.catch(error => error);
const fetchData = id => chart_request(id);
I have modified the SandBox so you can test your output.

Resources