React use declared const in other file - html

I've tried to search around for a solution, but I can't find anything that helps.
I want to be able to take the details from the api and be able to pass it's information to another page. The name is currently defined like "drink.strDrink" and so is the image and so on.
But I want to get the date in another document after fetching it, here's what I've got.
// Here I get the data from the API
const getDrink = () => {
Axios.get(`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${id}`).then((res)=> {
console.log(res)
setDrinkList(res.data.drinks)
})
}
// Here I use the data from the API
<Fragment key={index} >
<Card.Img variant="top" src={drink.strDrinkThumb} />
<Card.Body>
<Card.Title>{drink.strDrink}</Card.Title>
<Card.Text style={{overflow: 'hidden', whiteSpace: 'pre'}}>
<p>{drink.strCategory}</p>
</Card.Text>
<Router>
<Button variant="primary"><Link to={"/drink#"+drink.strDrink.replace(/\s+/g, '-')}>More info</Link></Button>
</Router>
</Card.Body>
</Fragment>

You can either use a state management tool like redux or the context api:
https://react-redux.js.org/
https://reactjs.org/docs/context.html
Or you can lift state up and pass the state as props:
https://reactjs.org/docs/lifting-state-up.html

Related

How to get and display specific data from onClick into another page in React

So I am currently working on a react project and I am trying to figure out how to get specific data from a clicked button/profile and then display that onto another page.
This is what I currently have. Here is a list of profiles of different celebrities currently displayed on a home page. Each one is shown as a profile card that you can click on.
import React from 'react';
import '../styles/Profile.css';
import ProfileItems from '../components/ProfileItems';
function profile() {
return (
<div className='profile'>
<h1>Choose A Profile</h1>
<div className='profile__container'>
<div className='profile__wrapper'>
<ul className='profile__items'>
<ProfileItems
src='images/KimK.jpg'
text='Kim Kardashian - Skims'
label='Social Media Influncer'
path='/'
/>
<ProfileItems
src='images/TheRock.jpg'
text='The Rock - Zoa Energy Drink'
label='Pressional Wrestler & Actor'
path='/'
/>
</ul>
<ul className='profile__items'>
<ProfileItems
src='images/Ronaldo.jpg'
text='Cristiano Ronaldo - Portugal Team'
label='Professional Soccer Player'
path='/'
/>
<ProfileItems
src='images/Kylie.jpg'
text='Kylie Jenner - Kylie Cosmetics'
label='Social Media Influncer'
path='/'
/>
<ProfileItems
src='images/Elon.jpg'
text='Elon Musk - Tesla'
label='CEO'
path='/'
/>
</ul>
</div>
</div>
</div>
);
}
export default profile;
Now for each of these profiles, when they are clicked on I want them to be specifically routed or set to a path to another page with their respective data. For example, I have this JSON file that contains information about Kylie Jenner and Kim Kardashian. So when I click on Kylie Jenner's profile, I would like for data to be pulled from the JSON file and to ONLY pull Kylie's data and display that data on another page to which I have set as the "Analysis Page".
[
{
"id": 1,
"title": "Kylie Jenner",
"content": "Social Media Public Figure",
"disclaimer": "*Disclaimer: This user could have comment filtering turned on",
"slug": "hello-world",
"img" : "https://ilarge.lisimg.com/image/16801290/1080full-kylie-jenner.jpg"
},
{
"id": 2,
"title": "Kim Kardashian",
"content": "Social Media Public Figure",
"disclaimer": "*Disclaimer: This user could have comment filtering turned on",
"slug": "hello-world",
"img" : ""
}
]
The same would go for another celebrity such as Kim Kardashian. So when I click on Kim's profile, only her data would be pulled from the JSON file. Once that is pulled, I would like that to be displayed on the "Analysis Page" that I have set up below.
import React, { Component } from 'react'
export class Analysis extends Component {
render() {
return (
<div>
</div>
)
}
}
export default Analysis
I believe I have to set up an onClick function, but I am just confused on how/where to set up that onClick function to grab the specific data. And then routing the profile and displaying that onto another page. If someone can please guide me or suggest an easier method, please let me know. Would be greatly appreciated.
You need to use react-router-dom to pass the celebrity name (title in your case) in props. Then, in your Analysis page, with the props you retrieve the associated informations from your database.
Check my demo here : https://stackblitz.com/edit/react-gkpswn?file=src%2FApp.js
ProfileItems.js
// Link to analysis route with title in props
<Link to={`/analysis/${title}`}>
App.js
// Route to analysis page/component with title props
<Route path="/analysis/:title" render={props => <Analysis {...props} />} />
Analysis.js
// Get the props with useParams (react-router-dom)
const { title } = useParams();
// filter data to get informations based on props "title"
const profile = Data.filter(profile => profile.title === title);

React App crashes sometimes when opening react-signature-canvas

Sorry, I can't be very specific with the details of the problem as it only happens sometimes, and I haven't been able to recreate it, which means I have no clue where to start trying to fix it.
It appears to only happen on really cheap android tablets.
I have a page with a form where the user fills in details, The problem happens just after they have entered their name into a text field and then once they press onto the react-signature-canvas to start drawing their signature the app crashes (doesn't crash all the time).
in the past, I think the crash was caused when the keyboard was still open when the user tried to start drawing on the signature pad.
As I said, I'm finding it really difficult to fix as I can't recreate it, so any help at all would be greatly appreciated.
I'm using React Hooks and Formik.
Form:
<h2>Guardian Full Name</h2>
<MyTextField
label="Guardian Full Name"
name="parentName"
required
/>
<ErrorMessage
component={"div"}
className={"termsConditionText error"}
name={"parentSignature"}
/>
<SignaturePad setFieldValue={setFieldValue} />
SignaturePad:
import React, { useRef, useState } from "react";
import { Button } from "semantic-ui-react";
import "../../pages/SignDisclaimerForm/SignDisclaimerForm.css";
import "./signaturePad.css";
import SignatureCanvas from "react-signature-canvas";
export const SignaturePad = props => {
const [canvasImageUrl, setCanvasImageUrl] = useState([
props.parentSignature || ""
]);
let sigCanvas = useRef();
const clearCanvas = () => sigCanvas.current.clear();
const saveCanvas = async () => {
if (sigCanvas.current.isEmpty()) return;
document.getElementById("parentName").blur();
props.setFieldValue(
"parentSignature",
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
setCanvasImageUrl(
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
};
return (
<div>
{!props.disabled && (
<div>
<h2 style={{ marginLeft: "5%" }}>Guardian Signature</h2>
<div className={"sigContainer"}>
<SignatureCanvas
ref={sigCanvas}
canvasProps={{ className: "sigPad" }}
onEnd={saveCanvas}
/>
</div>
<Button
style={{ marginLeft: "5%", marginTop: "2%", marginRight: "2%" }}
type={"button"}
onClick={clearCanvas}
children={"Clear"}
/>
<br />
<br />
</div>
)}
{canvasImageUrl[0] && (
<div className={"signatureDisplay"}>
<img
src={canvasImageUrl}
alt={"Guardian Signature"}
style={{ height: "100%", width: "100%" }}
/>
</div>
)}
</div>
);
};
Sentry issue report also below.
Issue Title:
TypeError HTMLCanvasElement.r(src/helpers)
error
Cannot read property 'push' of undefined
Issue Body:
../../src/helpers.ts in HTMLCanvasElement.r at line 85:17
}
// Attempt to invoke user-land function
// NOTE: If you are a Sentry user, and you are seeing this stack frame, it
// means the sentry.javascript SDK caught an error invoking your application code. This
// is expected behavior and NOT indicative of a bug with sentry.javascript.
return fn.apply(this, wrappedArguments);
// tslint:enable:no-unsafe-any
} catch (ex) {
ignoreNextOnError();
withScope((scope: Scope) => {
Bread Crumbs:
This is what the form looks like:
Formik author here...
You might be setting state from an unmounted DOM element (the canvas). It doesn't happen all the time because it's a race condition. You should check whether the canvas ref is actually mounted before using methods on it within your callbacks.
// ...
const sigCanvas = useRef(null);
const clearCanvas = () => {
if (sigCanvas.current != null) {
sigCanvas.current.clear();
}
};
const saveCanvas = async () => {
// Ensure that the canvas is mounted before using it
if (sigCanvas.current != null) {
if (sigCanvas.current.isEmpty()) return;
document.getElementById("parentName").blur();
props.setFieldValue(
"parentSignature",
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
setCanvasImageUrl(
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
}
};
// ...
Thank you to everyone who helped me, I really appreciated it.
What I did in the end to fix the problem was just to have a green button the user had to press in order to open the signature pad.
The fact that the user has to press the open button, gives the keyboard enough time to completely dismiss before the user starts to draw on the signature pad.
Thank you :)

how to parse values from JSON.stringified data in a text component sent from a different page

with regard to this tutorial "React Router Native - Passing Data" https://www.youtube.com/watch?v=SdOWxoH3HLg by #benawad user:4272160
I can't figure out how to extract bob or 5 from
{"val1:"bob","val2":5}
from the stringified string data in <Text>JSON.stringify(location.state)}</Text>
when passing data between pages
I've tried to contact #benawad through a comment, I've searched google and here for similar but found nil relevant. I tried a regex unsuccessfully but there has to be a better way anyway...
code is at https://github.com/benawad/react-router-native-example/tree/1_advanced
// Home.js
import React from "react";
import { View, Text, Button } from "react-native";
export default ({ history }) => (
<View>
<Text>This is the home page</Text>
<Button title="change page" onPress={() =>
history.push("/products", {val1: "bob", val2: 5})
/>
</View>
);
// Products.js
import React from "react";
import { View, Text, Button } from "react-native";
export default ({ history, location }) => (
<View>
<Text>Product 1</Text>
<Text>Product 2</Text>
<Text>{JSON.stringify(location.state)}</Text>
<Button title="change page" onPress={() => history.push("/")}/>
</View>
);
I thought about trying to JSON.parse the stringified data. No joy. I tried location.state.val but just got
TypeError: undefined is not an object (evaluating 'location.state.val')
You need to pass state through the history api. Change your Home component to
export default ({ history }) => (
<View>
<Text>This is the home page</Text>
<Button title="change page" onPress={() =>
history.push("/products", {val1: "bob", val2: 5)} />
</View>
);
and then access location.state.val1 and location.state.val2 directly in your Products component.
See https://github.com/benawad/react-router-native-example/blob/1_advanced/ChangePageButton.js#L9 for the similar history line in the tutorial code.
The JSON.stringify used in the example code is just there as an illustration so that the entire location.state can be displayed in a Text element.
You can pass props to history object,
<Button title="change page"
onPress={() => history.push({
pathname: '/',
state: { locationData: JSON.stringify(location.state) } //JSON.stringify needed if you want to pass object otherwise don't use.
})}
/>
In the component which is rendered with / route, you can access the props like,
{JSON.parse(props.location.state.locationData)} //Functional component
{JSON.parse(this.props.location.state.locationData)} //Class based component
Note: JSON.parse needed if object is being passed otherwise no need to use.

Add a class to the HTML <body> tag with React?

I'm making a modal in my React project that requires a class to be added to the body when the modal is open and removed when it is closed.
I could do this the old jQuery way by running some vanilla JavaScript which adds / removes a class, however this doesn't feel like the normal React philosophy.
Should I instead setState on my top level component to say whether the modal is open or closed? Even if I did this, as it's rendered into the div on the page it's still a side-effect to edit the body element, so is there any benefit for this extra wiring?
TL;DR use document.body.classList.add and document.body.classList.remove
I would have two functions that toggle a piece of state to show/hide the modal within your outer component.
Inside these functions I would use the document.body.classList.add and document.body.classList.remove methods to manipulate the body class dependant on the modal's state like below:
openModal = (event) => {
document.body.classList.add('modal-open');
this.setState({ showModal: true });
}
hideModal = (event) => {
document.body.classList.remove('modal-open');
this.setState({ showModal: false });
}
With the new React (16.8) this can be solved with hooks:
import {useEffect} from 'react';
const addBodyClass = className => document.body.classList.add(className);
const removeBodyClass = className => document.body.classList.remove(className);
export default function useBodyClass(className) {
useEffect(
() => {
// Set up
className instanceof Array ? className.map(addBodyClass) : addBodyClass(className);
// Clean up
return () => {
className instanceof Array
? className.map(removeBodyClass)
: removeBodyClass(className);
};
},
[className]
);
}
then, in the component
export const Sidebar = ({position = 'left', children}) => {
useBodyClass(`page--sidebar-${position}`);
return (
<aside className="...">
{children}
</aside>
);
};
Actually you don't need 2 functions for opening and closing, you could use document.body.classList.toggle
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
document.body.classList.toggle('modal-open', isOpen);
},[isOpen])
<button onCLick={()=> setIsOpen(!isOpen)}>Toggle Modal</button>
Like what #brian mentioned, try having a top-level container component that wraps around your other components. (assuming you're not using redux in your app)
In this top-level component:
Add a boolean state (eg. modalOpen) to toggle the CSS class
Add methods (eg. handleOpenModal & handleCloseModal) to modify the boolean state.
Pass the methods created above as props into your <Modal /> component
ReactJS has an official React Modal component, I would just use that: https://github.com/reactjs/react-modal

Navigate in code with react-router-dom 4.0?

Looking at this video the react router seems easy to use, but I can't find how to navigate in my code since I want to link on clicking a div and not use <Link>.
I've search StackOverflow but haven't found any answers that work with 4.0. Trying to import browserHistory gives undefined (before and after installing 'react-router' in addition to 'react-router-dom') from this question:
import { browserHistory } from 'react-router';
console.log('browserHistory:', browserHistory);
I also saw somewhere that there is a 'context' you can get to, but this shows a value for 'match' but not 'context':
<Route path="/" render={({ match, context}) => {
console.log('match:', match);
console.log('context:', context);
Edit
In the dev tools I can see that "Router" has a history property, so when I add that I can get to it:
<Route path="/" render={({ match, context, history}) => {
Is there a way to get to this from outside a route? For example a navbar component that will navigate to other components, but is not inside a Route itself...
If I understand your question, this is how you make a link programaticaly.
class Test extends React.Component {
handleClick() {
console.log(this.context);
this.context.router.history.push('/some/path');
},
render() {
return (
<div onClick={handleClick}>
This is div.
</div>
)
}
}
Test.contextTypes = {
router: React.PropTypes.object.isRequired
}
ReactDOM.render(
<Test />,
document.getElementById("app")
);
Had to read into the docs more. The history object is only passed as a property using the component (or other) attributes on a Route. Apparently need to include the 'history' package and use createBrowserHistory and pass it to the Router, then specify the component in a Route. I think this should work fine since exact isn't specified...
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
ReactDOM.render( (
<Router history={ history }>
<Route path="/" component={ App } />
</Router>
),
document.getElementById('root')
);
Before I just had <App/> inside <Router> and didn't have access to those properties.
Why don't you just wrap your div in the link instead of trying to circumvent it and make your life easier?
<Link to="/" >
<div className="to-component">go to component</div>
</Link>