How do i use the MS graph api in react? - javascript

I'm completely new to JS and React and im trying to upload a file with my MS custom teams app.
I've found the information i need to make it work, i just dont understand how i can use it within my teams tab.
import React from 'react';
import './App.css';
import * as microsoftTeams from "#microsoft/teams-js";
class Tab extends React.Component {
constructor(props){
super(props)
this.state = {
context: {}
}
}
componentDidMount() {
new Promise((resolve) => {
microsoftTeams.getContext(resolve);
})
.then((context) => {
this.setState({ context });
//var inputs {}
const queryParameters = new URLSearchParams({ function: "getDocuments", input: '"'+ context.userPrincipalName + '"',});
console.log(`userPrincipalName is '${context.Id}'`);
console.log(`teamName is '${context.teamName}'`);
console.log(`http://localhost/openims/json.php?${queryParameters}`);
return fetch(`http://localhost/openims/json.php?${queryParameters}`);
})
.then((res) => res.json())
.then((result) => this.setState({ ...result }))
.catch((error) => this.setState({ error }))
.finally(() => this.setState({ isLoaded: true }));
}
render() {
const { error, isLoaded, name, age, city } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<ul>
<li>
{/* Your Link */}
{name} {age} {city}
</li>
</ul>
);
}
}
}
export default Tab;
Currently im using a componentDidMount to fetch some info i need from a URL, but now i need to figure out how i add another componentDidMount(i think) to do a PUT and upload a file to my drive location. Preferably the drive location of my MS teams team onedrive.
So somewhere i have to put this:
PUT /me/drive/root:/FolderA/FileB.txt:/content
Content-Type: text/plain
The contents of the file goes here.
So i can actually upload a file. How do i go about this?

You can not add multiple componentDidMount() methods however in success callback you can call another API to upload the file.
Or you can call after promise in same componentDidMount() method.
Also you can write your code like below:
fetch('https://me/drive/root:/FolderA/FileB.txt:/', {
method: 'PUT',
body: fileContent
})
.then((response) => response.json())
.then((result) => {
console.log('Success:', result);
})
.catch((error) => {
console.error('Error:', error);
});
You can refer below documentation:
https://learn.microsoft.com/en-us/graph/api/driveitem-put-content?view=graph-rest-1.0&tabs=http#example-upload-a-new-file
Similar issue reference URL:
How do I upload a file with the JS fetch API?

Related

how to fetch url that contains an array dynamically

im having trouble figuring out how to fetch a url that contains an array in react
the parent component fetches data that gets sent to two components.
export default class ParentComponent extends Component<AuthProps, ChannelState> {
constructor(props: AuthProps) {
super(props)
this.state = {
...
}
}
getChannel = () => {
console.log("get channel called")
fetch(`${APIURL}/channel/mine`, {
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
"Authorization": `${this.props.sessionToken}`
})
})
.then(response => response.json())
.then(data => {
console.log(data)
this.setState({
channel: data
})
console.log(this.state.channel, "channel called")
})
.catch(err => console.log(err))
}
the state gets sent to two child components. childcomponent1 is a route that uses channelId in the fetch method. childcomponent2 displays a dynamic link to component1 using channelId as a key
export default class ChildComponent1 extends Component<AuthProps, ChannelEntryState> {
constructor(props: AuthProps) {
super(props)
this.state = {
...
}
}
getChannelEntry = () => {
console.log("get channel entry called")
console.log(this.props.channel.length)
fetch(`${APIURL}/channel/${this.props.channel[1].channelId}/channelentry`, {
method: "GET",
headers: new Headers({
"Content-Type": "application/json",
"Authorization": `${this.props.sessionToken}`
})
})
.then(response => response.json())
.then(data => {
console.log(data)
this.setState({
channelEntry: data.messages
})
console.log(this.state.channelEntry, "channel entry called")
})
.catch(err => console.log(err))
}
const ChildComponent2 = (props: AuthProps) => {
return(
<Row>
{props.channel.map((cprops: ChannelType) => {
return(
<>
<Col>
<div>
<ul className="sidebar-list list-unstyled" key={cprops.channelId}>
<li><Link to={`/channelEntry/${cprops.channelId}`}><Button onClick={() => {console.log('button clicked')}}>{cprops.name}</Button></Link></li>
</ul>
</div>
</Col>
</>
)
})}
Ive looked into useParams but i believe its only possible in a functional component. I believe i shouldnt use functional components when states can change. How can i fetch the url in component1 dynamically.
Concerning params you can access them in a react class component using this.props.match.params.
And concerning the useParams, two things
yes, anything with a use in front of a name should be a hook and can only be used in functional components.
no, functional components, since React v17, can have their own states, using the useState hook.
just keep in mind that the you can have multiple states in functional components so you should use a state for each controlled part.
for people using react typescript class components look into this link
React-router-v6 access a url parameter

onClick function requires two clicks to update state

I'm trying to create an app that has favoriting functionality.
The functionality actually works but only after the second click.
Edit for additional information: On the first click, the app tries to send information to my database but it's incomplete. It's only sending my initial values. On the second click, it finally appends the additional required values and gets saved to the database.
I'm pretty new to hooks so please feel free to comment on any improvement my code can use.
Here is my React component that is handling everything.
import React, { useState, useEffect } from 'react';
import { Button } from 'react-bootstrap';
const ItemInfo = (props) => {
const [item, setItem] = useState([]);
const [ favorite, setFavorite ] = useState({favoritable_type: 'Item', favoritor_type: 'User' })
useEffect(() => {
fetch(`/items/${props.match.params.id}`)
.then((response)=>{
if(response.status === 200){
return(response.json())
}
})
.then((item) => {
setItem(item);
})
}, []);
const createFavorite = (data) => {
fetch(`/favorites`, {
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': props.token
},
method: 'POST'
})
.then((resp) => {
if (resp.ok) {
setFavorite({})
alert('This items has been favorited!')
}
})
.catch((err) => {
if (err) {
console.log(err)
}
})
}
const handleFavorite = () => {
const value = { favoritor_id: props.current_user.id, favoritable_id: item.id }
setFavorite(favorite => ({...favorite, value}));
createFavorite(favorite)
}
If I'm being completely honest, I'm not exactly sure what this is doing.
I came across an answer that had something similar and it seems to fix the issue in the console but not in the actual POST attempt.
useEffect(()=> {
console.log(favorite)
},[favorite])
All this seems to work fine.
if (item === null) {
return <div>Loading...</div>
}
return (
<React.Fragment>
<Button onClick={ handleFavorite } >Favorite</Button>
<h1>{item.title}</h1>
<p>{item.body}</p>
<p>{item.user_id}</p>
</React.Fragment>
);
};
export default ItemInfo;
I've tried separating some of the functionality into different components, recreating it using a class component and have not been able to figure it out.
Thanks in advance!
Since hook values are constants, they don't change immediately with the setHookName, they change on the next render. You can get somewhat around this by injecting code into the setHookName function like this:
const handleFavorite = () => {
setFavorite(oldFavorite => {
const value = { favoritor_id: props.current_user.id, favoritable_id: item.id };
const newFavorite = {...oldFavorite, ...value};
createFavorite(newFavorite);
return newFavorite;
});
}

How to fetch a single data base entry with Redux and React?

I'm learning React and Redux. And I may have a really basic question.
I want to get a single story from my backend using the Redux function mapStateToProps (#1). So I wrote the function getSingleStory which takes the id as argument and returns the story data (#2). When I log the response data of the getSingleStory in the console, it shows me the correct story fetched from the backend (#3):
However, if the console logs the story array in my component (#4), it outputs all stories from my database, not just the single story I wanted to fetch (see picture). If I want to display 'Story.title', in my render function of course it does not work.
If someone could explain to me why in the response data the single story is included and in the const story = this.props.story; all stories suddenly appear, that would help me a lot.
export class StoryDetails extends Component {
componentDidMount() { // #2
this.props.getSingleStory(this.props.match.params.id);
}
render() {
const story = this.props.story;
console.log (story); // #4
return (
<div>
<h2>{story.title}</h2>
</div>
);
}
}
const mapStateToProps = state => ({story: state.story}); //#1
export default connect(
mapStateToProps,
{ getSingleStory, deleteStory}
)(StoryDetails);
Action
// GET SINGLE STORY
export const getSingleStory = id => (dispatch, getState) => {
return new Promise((resolve, reject) => {
axios.get( apiBase + `/story/${id}/`, tokenConfig(getState))
.then(res => {
dispatch({
type: GET_SINGLE_STORY,
story: res.data
}, console.log (res.data)); //#3
resolve(res);
})
.catch(err => {
dispatch(returnErrors(err.response.data, err.response.status));
reject(err);
});
});
};
Reducer
import { GET_SINGLE_STORY } from "../actions/types.js";
export default function (state = {}, action) {
switch (action.type) {
case GET_SINGLE_STORY:
return action.story;
default:
return state;
}
};
Many Thanks in advance!

How do I access json using reactjs from expressjs?

I am new to reactjs and expressjs. How do I get the data from reactjs and store it in a variable.
So far I am able to do res.send the data.
app.get('*', (req, res) => {
const data = {hello: world};
res.send(data);
});
This sends the data to the browser and displays but I want to just save the data to a variable instead of displaying it.
This is React.js example
import axios from 'axios'
click () {
axios.get('yourAPIAdress')
.then(response => console.log(response))
}
and this is your node.js example code;
const https = require('https');
https.get('yourAPIAdress', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
console.log(JSON.parse(data).explanation);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
You can in your React Component do something like:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(){
super();
this.state ={users: []};
}
componentDidMount() {
fetch('/users')
.then(users => {
console.log(users);
this.setState({ users })
});
}
render() {
return (
<div className="App">
<h1>Users</h1>
{this.state.users.map(user =>
<div key={user.id}>user: {user.name} Password: {user.password}</div>
)}
</div>
);
}
}
export default App;
Assuming the object you're interested in called "users"
(* You need to change your JSX according to your object fields for sure, to test this)

React - Render HTML When Setting a State After Fetching Data

I have an application which needs to fetch invoice data from Stripe API (payment processor). When the invoice data has been returned, I'm trying to update my state using this.setState({invoiceData: invoices}) where invoices is a string of HTML that I build out from the data returned from the Stripe API.
The issue is that the HTML isn't being rendered and is showing as plain text. I am pretty new to React and have only just got my head around rendering states, but now I'm pretty stuck on working this one out. What do I need to do to render the HTML? Please see my code below.
import React from 'react';
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
this.state = {
invoiceData: false
}
}
// When the 'BillingInvoices' component is mounted:
componentDidMount() {
// Get invoice data from Stripe API.
fetch('/stripe-invoices', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
customerId: '128973982'
})
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
console.log('Error with Stripe response');
}
})
.then((stripeData) => {
var invoiceCount = stripeData['result']['data'].length;
var i;
var invoices = '';
for (i = 0; i < invoiceCount; i++) {
invoices += '<div><a href="' + stripeData['result']['data'][i]['invoice_pdf'] + '" download>' + stripeData['result']['data'][i]['number'] + '</a></div>';
}
this.setState({
invoiceData: invoices
})
})
.catch((error) => {
console.log('Error: ', error);
});
}
render() {
return (
<div id="billing-invoices">
{this.state.invoiceData ? this.state.invoiceData : null}
</div>
);
}
}
export default BillingInvoices;
Thank you for any insight.
I've stripped out some of your code for my example to make it easier to read:
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
this.state = { invoiceData: [] }
}
componentDidMount() {
fetch('/stripe-invoices')
.then((response) => response.ok && response.json())
// Here I'm assigning the nested array to `invoiceData` immediately
// so that you don't need to map over it later
.then((data) => this.setState({ invoiceData: data.result.data }));
}
render() {
// Here we can check if the data exists. If it doesn't
// show a loading icon (or something) until it is
if (!this.state.invoiceData) <Loader />
// ...otherwise show the data
return (
<div id="billing-invoices">
// we map over the invoice data and for each invoice
// return JSX (your div with an anchor populated with that invoice data)
{this.state.invoiceData.map((invoice) => {
return (
<div>
<a href={invoice.invoice_pdf} download>{invoice.number}</a>
</div>
)
})}
);
</div>
)
}
}
You can populate invoiceData with react components using JSX like so:
let invoices = (<div>{stripeData['result']['data'].map(data => (<div><a href={data['invoice_pdf']} download>{data['number']}</a></div>))}</div>);
this.setState({invoiceData: invoices});
You can replace the content of the second then clause with the above and leave the rest of the code unchanged.
Putting the resulted json in the component state is a good idea.
But then, you should deal with this json directly in your render method, using the power of JSX.
Check the official documentation about how to use JSX.
This is a dummy example of what your component could look like with the usage of JSX:
import React from "react";
class BillingInvoices extends React.Component {
constructor(props) {
super(props);
}
state = {
invoices: []
}
// When the 'BillingInvoices' component is mounted:
componentDidMount() {
// Get invoice data from Stripe API.
fetch("/stripe-invoices", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
customerId: "128973982"
})
})
.then(response => {
if (response.ok) {
this.setState(invoices: response.json());
} else {
console.log("Error with Stripe response");
}
})
.catch(error => {
console.log("Error: ", error);
});
}
render() {
return (
<div id="billing-invoices">
{this.state.invoices.map((invoice, index) => {
return (
<div key={index}>{invoice.name}</div>
)
})}
</div>
);
}
}
export default BillingInvoices;

Categories