Based on this question:
https://stackoverflow.com/a/43133108
I have a similar issue but in react used with material-ui.
Since authorization implemented the export is not working as it is a navigation link, it throws a 401 authorization required.
Created an export button comp (MenuButton is from mui, component: RouterLink is react-router-dom v5):
const ExportButton = ({ items, ...props }) => {
const { search } = useLocation();
const menuItems = useMemo(
() =>
items.map((item) => ({
key: item.type,
primaryText: item.type,
component: RouterLink,
target: '_blank',
to: `${item.url}${search}`,
onClick: () => null,
})),
[items, search],
);
return (
<MenuButton
secondary
inverse
text="EXPORT"
menuItems={menuItems}
/>
);
};
in use:
<ExportButton
items={[
{
type: 'XLSX',
url:'some-url-name',
},
{
type: 'CSV',
url:'some-url-name',
},
]}
/>
so the question is how to use in this case the logic that was mentioned on the question linked at the top:
let anchor = document.createElement("a");
document.body.appendChild(anchor);
let file = 'https://www.example.com/some-file.pdf';
let headers = new Headers();
headers.append('Authorization', 'Bearer MY-TOKEN');
fetch(file, { headers })
.then(response => response.blob())
.then(blobby => {
let objectUrl = window.URL.createObjectURL(blobby);
anchor.href = objectUrl;
anchor.download = 'some-file.pdf';
anchor.click();
window.URL.revokeObjectURL(objectUrl);
});
Related
I'm referring to this post (Write to a text or JSON file in react with node).
I got the exact problem so I tried out the solutions. I don't understand the solutions very well. I spent a lot of time doing by trial and errors. For example, in this solution:
const handleSaveToPC = jsonData => {
const fileData = JSON.stringify(jsonData);
const blob = new Blob([fileData], {type: "text/plain"});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.download = 'filename.json';
link.href = url;
link.click();
}
Where should I put this code? The variable "handleSaveToPC" is declared but not used. How should I use it? I show my App.js and index.js files below. My goal is to write the contactsData array to a local json or text file. I do appreciate if anyone guide me exactly what I have to do. Thank you so much.
App.js
import React, { useState, Fragment } from "react";
import AddContactForm from "./forms/AddContactForm";
import EditContactForm from "./forms/EditContactForm";
import ContactTable from "./tables/ContactTable";
const App = () => {
const contactsData = [
{ id: 1, organization: "A Ltd.", name: "Carol", email: "carol#gmail.com" },
{ id: 2, organization: "B Ltd.", name: "Paul", email: "paul#gmail.com" },
{ id: 3, organization: "C Ltd.", name: "Emily", email: "emily#gmail.com" }
];
const initialFormState = { id: null, organization: "", name: "", email: "" };
const [contacts, setContacts] = useState(contactsData);
const [currentContact, setCurrentContact] = useState(initialFormState);
const [editing, setEditing] = useState(false);
const addContact = (contact) => {
contact.id = contacts.length + 1;
setContacts([...contacts, contact]);
};
const deleteContact = (id) => {
setEditing(false);
setContacts(contacts.filter((contact) => contact.id !== id));
};
const editRow = (contact) => {
setEditing(true);
setCurrentContact({
id: contact.id,
organization: contact.organization,
name: contact.name,
email: contact.email
});
};
const updateContact = (id, updatedContact) => {
setEditing(false);
setContacts(
contacts.map((contact) => (contact.id === id ? updatedContact : contact))
);
};
return (
<div className="container">
<h1>Address Book</h1>
<div className="flex-row">
<div className="flex-large">
{editing ? (
<Fragment>
<h2>Edit contact</h2>
<EditContactForm
editing={editing}
setEditing={setEditing}
currentContact={currentContact}
updateContact={updateContact}
/>
</Fragment>
) : (
<Fragment>
<h2>Add contact</h2>
<AddContactForm addContact={addContact} />
</Fragment>
)}
</div>
<div className="flex-large">
<h2>View contacts</h2>
<ContactTable
contacts={contacts}
editRow={editRow}
deleteContact={deleteContact}
/>
</div>
</div>
</div>
);
};
export default App;
index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));
EDIT: As you're trying to download a file through the frontend, you're able to use your function pretty much anywhere. Just add for example a button, like this:
<button onClick={handleSaveToPC}>download</button>
and change your function to:
const handleSaveToPC = () => {
const fileData = JSON.stringify(contactsData);
const blob = new Blob([fileData], {type: "text/plain"});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.download = 'filename.json';
link.href = url;
link.click();
}
Original answer:
I suppose you're using React for the frontend and node for the backend. If so, you could send the data to a backend route, and there write the data to the PC with this function.
I'd suggest using fs.
This is an example function for how you could do it.
const fs = require('fs')
const writeToJSON = (data) => {
const DATA_PATH = '/path/to/file'
fs.writeFile(
DATA_PATH,
JSON.stringify(data),
(err) => {
if (err) return console.error(err)
console.log('Wrote data to ', DATA_PATH)
},
)
}
I have a component and I want to fetch isbn data on button click using react hook useEffect, performing a get on the route ${basicUrl}/editorials/${isbn}, so i wrote this component:
import React, { Fragment, useState } from "react";
import "./Home.css";
import { V3_BASIC_URL } from "../../constants/endpoints";
import { useDataApi } from "../../store/effects/dataEffects";
import SearchIsbnElement from "../../components/SearchIsbnElement/SearchIsbnElement";
import IsbnPanelElement from "../../components/IsbnPanelElement/IsbnPanelElement";
function Home() {
const [query, setQuery] = useState<string>("9788808677853");
const [isValid, setIsValid] = useState<boolean>(true);
const url = `${V3_BASIC_URL(
process.env.REACT_APP_API_ENV
)}/editorials/${query}`;
const [{ isbn, isLoading, isError }, doFetch] = useDataApi(url, {
isLoading: false,
isError: false,
isbn: undefined,
});
const buttonCallback = () => {
doFetch(url);
};
const isbnRegexp = /^97\d{11}$/
const validateQuery = (query: string): boolean => isbnRegexp.test(query)
const inputCallback = (query: string) => {
setQuery(query)
setIsValid(validateQuery(query));
};
return (
<div id="isbn-panel-home" className="Home">
<SearchIsbnElement
inputCallback={inputCallback}
buttonCallback={buttonCallback}
query={query}
isValid={isValid}
></SearchIsbnElement>
{isError && <div>Il servizio al momento non è disponibile, riprova più tardi</div>}
{isLoading ? (
<div>Loading ...</div>
) : (
!isError &&
<Fragment>
<IsbnPanelElement isbn={isbn}></IsbnPanelElement>
<p>{isbn?.scheda_volume == null && 'isbn non trovato'}</p>
</Fragment>
)}
</div>
);
}
export default Home;
the useDataApi function uses the hook useEffect and returns state and setUrl action to set the new url on isbn value change. This is the useDataApi file:
import { useState, useEffect, useReducer } from "react";
import {
dataFetchFailure,
dataFetchInit,
dataFetchSuccess,
} from "../actions/dataActions";
import { dataFetchReducer, ISBNState } from "../reducers/dataReducers";
import { get } from "../../tools/request";
type InitialState = {
isLoading: boolean,
isError: boolean,
isbn: undefined,
}
export const useDataApi = (initialUrl: string, initialData: InitialState) : [ISBNState, (value: string) => void] => {
const [url, setUrl] = useState(initialUrl);
const [state, dispatch] = useReducer(dataFetchReducer, initialData);
useEffect(() => {
let didCancel: boolean = false;
const fetchData = async (): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`
}
}
try {
const {data} = await get(url, options);
if (!didCancel) {
dispatch(dataFetchSuccess(data));
}
} catch (error) {
if (!didCancel) {
dispatch(dataFetchFailure(error));
}
}
};
fetchData();
return () => {
didCancel = true;
};
}, [url]);
return [state, setUrl];
};
with this code fetching starts on page load, but i want to fetch data only on button click. How can I do this?
useEffect() is a hook to manipulate the component through the different lifecycle methods. In order to do something onClick you need to create a method for that:
const fetchData = async (): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`
}
}
try {
const {data} = await get(url, options);
if (!didCancel) {
dispatch(dataFetchSuccess(data));
}
} catch (error) {
if (!didCancel) {
dispatch(dataFetchFailure(error));
}
}
};
Just do that and you will be fine
Edit: the new version of useDataApi
export const useDataApi = (
url: string,
initialData: InitialState
): [ISBNState, (value: string) => void] => {
const [state, dispatch] = useReducer(dataFetchReducer, initialData);
const fetchData = useCallback(async (): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`,
},
};
try {
const { data } = await get(url, options);
dispatch(dataFetchSuccess(data));
} catch (error) {
dispatch(dataFetchFailure(error));
}
}, [url]);
return [state, fetchData];
};
The useDataApi hook returns [,doFetch], but the doFetch is actually setUrl so if you wanted that to work as expected you can let the initial value for the url be null or falsey and only allow a fetch inside the effect when the url is valid/truthy. When you click the button, thats when you setUrl and that's when the effect will allow a fetchData to occur because by then the value of url will be set.
export const useDataApi = (initialUrl: string, initialData: InitialState): [ISBNState, (value: string) => void] => {
// make this default to null here, or where you intende to use this hook
const [url, setUrl] = useState(null);
// custom hook body
useEffect(() => {
// effect body
if (url) {
fetchData();
}
// hook cleanup
}, [url]);
return [state, setUrl];
};
Although, the better solution is directly calling the function fetchData on the button click. One way you can do that is by modifying your useDataApi hook to return 'fetchData' directly allowing it to accept the url as an argument and removing the need for the const [url,setUrl] = useState(initialUrl) entirely
export const useDataApi = (initialUrl: string, initialData: InitialState): [ISBNState, (value: string) => void] => {
const [state, dispatch] = useReducer(dataFetchReducer, initialData);
const fetchData = useCallback(async (url): Promise<any> => {
dispatch(dataFetchInit());
const options = {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
auth: {
username: `${process.env.REACT_APP_API_AUTH_USER}`,
password: `${process.env.REACT_APP_API_AUTH_PWD}`,
},
};
try {
const { data } = await get(url, options);
if (!didCancel) {
dispatch(dataFetchSuccess(data));
}
} catch (error) {
if (!didCancel) {
dispatch(dataFetchFailure(error));
}
}
}, []);
return [state, fetchData];
};
You can also drop initialUrl from the hook useDataApi
I have a page that contains a component that renders a list from the results of a query. When I load the page the first time, the list renders fine. But whenever I go to another page and navigate back, an additional set of the result is appended to the list, creating duplicates in the DOM.
I'm not sure what I'm doing wrong here, but I don't want a new set items to be appended to the list every time I load the page.
apolloClient (https://github.com/vercel/next.js/tree/canary/examples/with-apollo)
let apolloClient;
const createApolloClient = () =>
new ApolloClient({
ssrMode: typeof window === "undefined",
link: new HttpLink({
uri: DB_URI,
credentials: "same-origin",
}),
cache: new InMemoryCache(),
});
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient();
if (initialState) {
const existingCache = _apolloClient.extract();
const data = merge(initialState, existingCache);
_apolloClient.cache.restore(data);
}
if (typeof window === "undefined") return _apolloClient;
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}
export function addApolloState(client, pageProps) {
if (pageProps?.props) {
pageProps.props[APOLLO_STATE_PROP_NAME] = client.cache.extract();
}
return pageProps;
}
export function useApollo(pageProps) {
const state = pageProps[APOLLO_STATE_PROP_NAME];
return useMemo(() => initializeApollo(state), [state]);
}
On my page I use getStaticProps as follows
export async function getStaticProps() {
const apolloClient = initializeApollo();
await apolloClient.query({
query: GET_THINGS,
});
return addApolloState(apolloClient, {
props: {},
revalidate: 1,
});
}
My list component looks as follows:
const ItemsList: React.FunctionComponent<Props> = (props) => {
const { loading, error, data } = useQuery(GET_THINGS, {});
const { items} = data;
const { filters } = props;
const [filteredItems, setFilteredItems] = useState(items);
useEffect(() => {
setFilteredItems(filterItems(items, filters));
}, [filters, items]);
const renderItems = (filteredItems: Array<Item>) =>
filteredItems.map((item) => (
<li key={item.id}>
<Link href={`/items/${item.id}`}>{item.name}</Link>
</li>
));
if (loading) return <div>"Loading...";</div>;
if (error) return <div>`Error! ${error.message}`;</div>;
return (
<div>
{filteredItems?.length > 0 ? (
<ul>{renderItems(filteredItems)}</ul>
) : (
<span>No items matched the criteria</span>
)}
</div>
);
};
export default ItemsList;
I noticed this strange behavior of my App, that when I do anything on it (write something in the search field, create a new list, etc) my page gets rerendererd. Of course, I cannot find the source of it.
Below is the the look of my page, when it is loaded the first time, with default (blank) search results.
And now, the result in profiler, when I type something in the searchBar (or create a new list, or anything):
Here is my code of the App.js
import React, { useState, createContext, useEffect } from "react";
import NavBar from "../NavBar/NavBar";
import youtube from "../../apis/youtube";
import VideoList from "../VideoList/VideoList";
import VideoDetail from "../VideoDetail/VideoDetail";
import SideBar from "../SideBar/SideBar";
import "./App.css";
export const VideoContext = createContext();
export const FavoriteContext = createContext();
const API_KEY = process.env.REACT_APP_API_KEY;
const App = () => {
const [ videos, setVideos ] = useState([]);
const [ searchedValue, setSearchedValue ] = useState({
selectedVideo: null
});
const handleSelectedVideo = (singleRenderedVideo) => {
setSearchedValue((previous) => ({
...previous,
selectedVideo: singleRenderedVideo
}));
};
const handleSearch = async (inputText) => {
const response = await youtube.get("/search", {
params: {
q: inputText,
part: "snippet",
type: "video",
maxResults: 16,
key: API_KEY
}
});
setVideos(response.data.items);
setSearchedValue({
selectedVideo: response.data.items[0] //take the first search result and make it appear as a playable one
});
};
useEffect(() => {
handleSearch();
}, []);
//By the user newly created lists
const [ lists, setLists ] = useState([]);
const addList = (newList) => {
setLists((prevLists) => {
return [ ...prevLists, newList ];
});
};
const onDeleteList = (id) => {
setLists((prevLists) => {
return prevLists.filter((listItem, index) => {
return index !== id;
});
});
};
//Render(Play) Favorited Video
const [ favoritedItem, setFavoritedItem ] = useState({
clickedFavoritedVideo: null
});
const handleSelectedFavorite = (renderFavorite) => {
setFavoritedItem((previous) => ({
...previous,
clickedFavoritedVideo: renderFavorite
}));
};
//Add a newly favorited video to a, by user created, list (BUG: for now the favorited video is added to EVERY, by the user, created list)
const [ favoritedList, setFavoritedList ] = useState([]);
const handleFavoritedVideo = (favoritedElement, selectedList) => {
setFavoritedList((previousFavorited) => {
return [ { favoritedElement, selectedList }, ...previousFavorited ];
});
};
const deleteFavorited = (id) => {
setFavoritedList((prevLists) => {
return prevLists.filter((listItem, index) => {
return index !== id;
});
});
};
return (
<div className="container">
<NavBar handleSearch={handleSearch} />
<div className="content">
<SideBar
addList={addList}
lists={lists}
handleSelectedFavorite={handleSelectedFavorite}
favoritedList={favoritedList}
onDeleteList={onDeleteList}
onDeleteFavorited={deleteFavorited}
/>
<main className="video">
<VideoContext.Provider value={handleSelectedVideo}>
<FavoriteContext.Provider value={handleFavoritedVideo}>
<VideoDetail
selectedVideo={searchedValue.selectedVideo}
clickedFavoritedVideo={
favoritedItem.clickedFavoritedVideo
}
/>
<VideoList listOfVideos={videos} lists={lists} />
</FavoriteContext.Provider>
</VideoContext.Provider>
</main>
</div>
</div>
);
};
export default App;
I will not post my whole app here, because it is a lot of files. I just give a link to my gitHub:
GitHub LINK
I was trying to find a solution, as stated here:
Link to SO page
which is like my case, but it didn't help (maybe because I was not using memo):
import React, { useState, createContext, useEffect, useCallback } from "react";
import NavBar from "../NavBar/NavBar";
import youtube from "../../apis/youtube";
import VideoList from "../VideoList/VideoList";
import VideoDetail from "../VideoDetail/VideoDetail";
import SideBar from "../SideBar/SideBar";
import "./App.css";
export const VideoContext = createContext();
export const FavoriteContext = createContext();
const API_KEY = process.env.REACT_APP_API_KEY;
const App = () => {
const [ videos, setVideos ] = useState([]);
const [ searchedValue, setSearchedValue ] = useState({
selectedVideo: null
});
const handleSelectedVideo = useCallback((singleRenderedVideo) => {
setSearchedValue((previous) => ({
...previous,
selectedVideo: singleRenderedVideo
}));
}, []);
const handleSearch = async (inputText) => {
const response = await youtube.get("/search", {
params: {
q: inputText,
part: "snippet",
type: "video",
maxResults: 16,
key: API_KEY
}
});
setVideos(response.data.items);
setSearchedValue({
selectedVideo: response.data.items[0] //take the first search result and make it appear as a playable one
});
};
useEffect(() => {
handleSearch();
}, []);
//By the user newly created lists
const [ lists, setLists ] = useState([]);
const addList = useCallback((newList) => {
setLists((prevLists) => {
return [ ...prevLists, newList ];
});
}, []);
const onDeleteList = useCallback((id) => {
setLists((prevLists) => {
return prevLists.filter((listItem, index) => {
return index !== id;
});
});
}, []);
//Render(Play) Favorited Video
const [ favoritedItem, setFavoritedItem ] = useState({
clickedFavoritedVideo: null
});
const handleSelectedFavorite = useCallback((renderFavorite) => {
setFavoritedItem((previous) => ({
...previous,
clickedFavoritedVideo: renderFavorite
}));
}, []);
//Add a newly favorited video to a, by user created, list (BUG: for now the favorited video is added to EVERY, by the user, created list)
const [ favoritedList, setFavoritedList ] = useState([]);
const handleFavoritedVideo = useCallback((favoritedElement, selectedList) => {
setFavoritedList((previousFavorited) => {
return [ { favoritedElement, selectedList }, ...previousFavorited ];
});
}, []);
const deleteFavorited = useCallback((id) => {
setFavoritedList((prevLists) => {
return prevLists.filter((listItem, index) => {
return index !== id;
});
});
}, []);
return (
<div className="container">
<NavBar handleSearch={handleSearch} />
<div className="content">
<SideBar
addList={addList}
lists={lists}
handleSelectedFavorite={handleSelectedFavorite}
favoritedList={favoritedList}
onDeleteList={onDeleteList}
onDeleteFavorited={deleteFavorited}
/>
<main className="video">
<VideoContext.Provider value={handleSelectedVideo}>
<FavoriteContext.Provider value={handleFavoritedVideo}>
<VideoDetail
selectedVideo={searchedValue.selectedVideo}
clickedFavoritedVideo={
favoritedItem.clickedFavoritedVideo
}
/>
<VideoList listOfVideos={videos} lists={lists} />
</FavoriteContext.Provider>
</VideoContext.Provider>
</main>
</div>
</div>
);
};
export default App;
I also tried to give a type for my buttons (type="button"), which currently have no type, like as in:
CreateNewList.js
import React, { useState } from "react";
import iconSprites from "../../images/sprite.svg";
import shortid from "shortid";
const CreateNewList = ({ onAdd }) => {
const [ list, setList ] = useState({
id: shortid.generate(),
title: ""
});
const handleChange = (event) => {
const { value } = event.target;
setList((prevList) => {
return {
...prevList,
title: value
};
});
event.preventDefault();
};
const submitNewList = (event) => {
onAdd({ ...list });
setList({ id: shortid.generate(), title: "" });
event.preventDefault();
};
return (
<React.Fragment>
<li className="new-list__item">
<form>
<div className="new-list__link">
<button
onClick={submitNewList}
className="new-list__btn-plus btn"
>
<svg className="new-list__icon">
<use href={iconSprites + "#icon-circle-with-plus"} />
</svg>
</button>
<input
className="new-list__input"
name="title"
value={list.title}
onChange={handleChange}
placeholder="New List"
/>
</div>
</form>
</li>
</React.Fragment>
);
};
export default CreateNewList;
but it also didn't help. Maybe because they are not in <form>?
So that is it. Maybe someone can help me with my issue?
To have all of the questions in one place:
Why my app keeps rerendering?
Should I use memo with useCallback?
Should I put my buttons in a <form> and give them a type?
I have a react child component rendering in a loop in parent component. e.g. in parent component I have as such:
<div className="md-grid">
{images
? images.map((img, index) => (
<PinnedImage
key={index}
name={img.mediaName}
picture={img.downloadURL}
imageSRC={this.createImageSrc(img.downlaodURL)}
onClick={this.downloadDoc.bind(
this,
img.downloadURL
)}
/>
))
: null}
</div>
I want to call a function in parent which is authorized fetch request to the file server using REST Endpoint. I am calling the function on imageSRC props of the child component. Below are the functions.
async createImageSrc (url) {
console.log('createImageSrc called', { url })
if (url) {
const downlaodURL = `${PROTOCOL}${url}`
console.log({ downlaodURL })
const token = localStorage.getItem('access_token')
const headers = new Headers({ Authorization: `Bearer ${token}` })
const options = {
method: 'GET',
headers,
mode: 'cors',
cache: 'default'
}
const request = new Request(downlaodURL)
const finalResponse = await fetch(request, options).then(response => {
response.arrayBuffer().then(buffer => {
const base64Flag = 'data:image/jpeg;base64,'
const imageStr = this.arrayBufferToBase64(buffer)
const imageSRC = base64Flag + imageStr
console.log({ imageSRC })
return imageSRC
})
})
console.log({ finalResponse })
return finalResponse
}
}
arrayBufferToBase64 (buffer) {
let binary = ''
const bytes = [].slice.call(new Uint8Array(buffer))
bytes.forEach(b => {
binary += String.fromCharCode(b)
})
return window.btoa(binary)
}
I wanted the result of this createImageSrc to be pass as PROPS to the child component through imageSRC={this.createImageSrc(img.downlaodURL)} But I am not getting as expected. What am I doing wrong? I am stuck.
Thanks
You're trying to use an asynchronous method in your render method.
What you want to do is instead move your call to createImageSrc from the render method into either componentDidUpdate or componentDidMount and make it so that your createImageSrc updates the state when fetching is done.
Here is pseudo code of what you should do
async function createImageSrc(url) {
const imageSRC = fetch();
return imageSRC;
}
class YourComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
imagesWithSrc: null
};
}
componentDidMount(props) {
if (props.images) {
this.fetchImageSrc(props.images);
}
}
fetchImageSrc = (images) => {
const promises = images.map((img) => createImageSrc(img.downloadURL));
Promise.all(promises).then((...imageSRCs) => {
const newImages = images.map((img, idx) => {
return {
...img,
imageSRC: imageSRCs[idx]
};
});
this.setState({ imagesWithSrc: newImages });
});
}
render() {
const { imagesWithSrc } = this.state;
return (
<div className="md-grid">
{ imagesWithSrc
? imagesWithSrc.map((img, index) => (
<PinnedImage
key={index}
name={img.mediaName}
picture={img.downloadURL}
imageSRC={img.imageSRC}
onClick={this.downloadDoc.bind(
this,
img.downloadURL
)}
/>
))
: null}
</div>
);
}
}
Side note just wanted to let you know that you spelled downloadURL wrong in a few places
the problem is child component will not know when promise will be fulfilled. you will have tell child component when promise fulfilled.
if you are using redux add image loaded to store where each child can get its source on re-render.
<div className="md-grid">
{images
? images.map((img, index) => (
<PinnedImage
key={index}
id={index[or some other unique id]}
name={img.mediaName}
picture={img.downloadURL}
imageSRC={this.createImageSrc(img.downlaodURL,id[same id used as child id])}
onClick={this.downloadDoc.bind(
this,
img.downloadURL
)}
/>
))
: null}
</div>
async createImageSrc (url,id) {
console.log('createImageSrc called', { url })
if (url) {
const downlaodURL = `${PROTOCOL}${url}`
console.log({ downlaodURL })
const token = localStorage.getItem('access_token')
const headers = new Headers({ Authorization: `Bearer ${token}` })
const options = {
method: 'GET',
headers,
mode: 'cors',
cache: 'default'
}
const request = new Request(downlaodURL)
const finalResponse = await fetch(request, options).then(response => {
response.arrayBuffer().then(buffer => {
const base64Flag = 'data:image/jpeg;base64,'
const imageStr = this.arrayBufferToBase64(buffer)
const imageSRC = base64Flag + imageStr
console.log({ imageSRC })
this.props.dispatch ({type:"imageLoaded",payload:{src:imageSRC,id:id}})
// return imageSRC
})
})
console.log({ finalResponse })
return finalResponse
}
}
arrayBufferToBase64 (buffer) {
let binary = ''
const bytes = [].slice.call(new Uint8Array(buffer))
bytes.forEach(b => {
binary += String.fromCharCode(b)
})
return window.btoa(binary)
}