In my app users click on button and navigate to external website. This external website is picked dynamically based on a form they fill.
Here, on clicking the button is not working.
What I am doing wrong here?
const handleLink1 =()=>{
<Link
to={{
pathname: `${data.url}`
}}
target="_blank"
>
{' '}
</Link>
}
<button onClick={handleLink1} className="btn-primary"> {data.buttonName}</button>``
Your handleLink1 should be like this.
const handleLink1 = () => {
window.open(data.url);
}
<button onClick={handleLink1} className="btn-primary"> {data.buttonName}</button>
I didn't really understand your code, but I don't think returning <Link> from click handler function is the correct solution. <Link> from react-router will only provide you ways to navigate around routes of your app.
To navigate to external url, you can either make use of <a> tag or window.location.href or window.open() .I believe you meant to do
const handleLink1 = () => {
window.open(data.url, '_blank');
}
<button onClick={handleLink1} className="btn-primary"> {data.buttonName}</button>
or instead of button directly use
<a
class='btn-primary'
href=`${data.url}`
target='_blank'
rel="noopener"
aria-label='Github'
>
{data.buttonName}
</a>
correct me if I'm mistaken :)
Could you provide the code for handleLink1?
Anyways, if handleLink1 is anything like handleLink2, your mistake is creating a Link component in the event handler. It'll never be rendered. What you have to do instead, is wrap your button component in the Link component, like so:
<Link
to="somewhere"
>
<button className="some-class">Click me to go somewhere!</button> //No need for the onClick event handler.
</Link>
Edit
Maybe the below will help clarify:
import { useState } from "react";
import { Link } from "react-router-dom";
const IllNeverRender = () => {
return <p>Nope</p>;
};
const IllRender = () => {
return <p>Yep</p>;
};
export default function LinkButton() {
const [Component, setComponent] = useState();
const renderOnClickWrong = (event) => {
<IllNeverRender />; // This component isn't returned from the handler, nor would it render if it was returned.
};
const renderOnClickRight = (event) => {
setComponent(<IllRender />);
};
return (
// Only components returned from a functional component, or a React Component's render method, gets rendered.
<div>
<Link to={{ pathname: "/somewhere" }}>
<button>Go somewhere!</button>
</Link>
<button onClick={renderOnClickWrong}>Go nowhere.</button>
<button onClick={renderOnClickRight}>Render something.</button>
{Component}
</div>
);
}
let home = "https://explorer.solana.com/tx/" + url;
window.open(home);
This is the perfect source code.
Related
I have a certain generic link component (an atom, I am using Atomic design for structuring my React app), which either returns just a <span> with a Title and arrow icon or when it does have a url it returns a react-router-dom <Link>.
I am using react-router-dom v5.
import React from 'react';
import { Link } from 'react-router-dom';
import { Arrow} from 'app/icons';
import { Props } from './MyLink.types';
const MyLink = ({ variant = 'iconRight', text, url }: Props) => {
const renderLinkBody = () => {
return (
<React.Fragment>
<span css={styles.label}>{text}</span>
<Arrow />
</React.Fragment>
);
};
return url !== undefined ? (
<Link to={url} title={text} >
{renderLinkBody()}
</Link>
) : (
<span>{renderLinkBody()}</span>
);
};
export default MyLink;
Now I want to use this component for creating a Back button (similar behaviour as the browser back button).
Is it a clean approach passing an onClick prop (function) from parent to child, something like:
const history = useHistory();
<MyLink text="My title" variant="iconLeft" onClick={history.goBack} />
and then adjust MyLink component a bit:
return url !== undefined || onClick ? (
<Link to={url} title={text} onClick={onClick} >
{renderLinkBody()}
</Link>
) : (
<span>{renderLinkBody()}</span>
);
I think this approach isn't bad. When attaching an onClick handler to the Link component you will likely need to prevent the default link behavior from occurring. You will also need to provide a valid to prop to the Link component when using a click handler.
Example:
const MyLink = ({ variant = "iconRight", onClick, text, url }) => {
const clickHandler = e => {
if (onClick) {
e.preventDefault();
onClick();
}
};
const renderLinkBody = () => {
return (
<>
<span css={styles.label}>{text}</span>
<Arrow />
</>
);
};
return url !== undefined || onClick ? (
<Link to={url || "/"} title={text} onClick={clickHandler} >
{renderLinkBody()}
</Link>
) : (
<span>{renderLinkBody()}</span>
);
};
Because of the issue with the onClick and the Link action, you may want to make the "back" navigation a button instead, and refactor MyLink to conditionally render the button, the link, or the span, depending on needs.
I'm doing a simple todo list using React. What I fail to do is to remove an item once I click on the button.
However, if I click delete and then add a new item, it's working, but only if I add a new todo.
Edit:I've edited the post and added the parent componenet of AddMission.
import React,{useState}from 'react';
import { Button } from '../UI/Button/Button';
import Card from '../UI/Card/Card';
import classes from '../toDo/AddMission.module.css'
const AddMission = (props) => {
const [done,setDone]=useState(true);
const doneHandler=(m)=>{
m.isDeleted=true;
}
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li className={mission.isDeleted?classes.done:''} key={mission.id}>
{mission.mission1}
<div className={classes.btn2}>
<Button onClick={()=>{
doneHandler(mission)
}} className={classes.btn}>Done</Button>
</div>
</li>
)) }
</ul>
</Card>
);
};
export default AddMission;
import './App.css';
import React,{useState} from 'react';
import { Mission } from './components/toDo/Mission';
import AddMission from './components/toDo/AddMission';
function App() {
const [mission,setMission]=useState([]);
const [isEmpty,setIsEmpty]=useState(true);
const addMissionHandler = (miss) =>{
setIsEmpty(false);
setMission((prevMission)=>{
return[
...prevMission,
{mission1:miss,isDeleted:false,id:Math.random().toString()},
];
});
};
return (
<div className="">
<div className="App">
<Mission onAddMission={addMissionHandler}/>
{isEmpty?<h1 className="header-title">Start Your Day!</h1>:(<AddMission isVisible={mission.isDeleted} missions={mission}/>)}
</div>
</div>
);
}
const doneHandler=(m)=>{
m.isDeleted=true;
}
This is what is causing your issue, you are mutating an object directly instead of moving this edit up into the parent. In react we don't directly mutate objects because it causes side-effects such as the issue you are having, a component should only re-render when its props change and in your case you aren't changing missions, you are only changing a single object you passed in to your handler.
Because you haven't included the code which is passing in the missions props, I can't give you a very specific solution, but you need to pass something like an onChange prop into <AddMission /> so that you can pass your edited mission back.
You will also need to change your function to something like this...
const doneHandler = (m) =>{
props.onChange({
...m,
isDeleted: true,
});
}
And in your parent component you'll then need to edit the missions variable so when it is passed back in a proper re-render is called with the changed data.
Like others have mentioned it is because you are not changing any state, react will only re-render once state has been modified.
Perhaps you could do something like the below and create an array that logs all of the ids of the done missions?
I'm suggesting that way as it looks like you are styling the list items to look done, rather than filtering them out before mapping.
import React, { useState } from "react";
import { Button } from "../UI/Button/Button";
import Card from "../UI/Card/Card";
import classes from "../toDo/AddMission.module.css";
const AddMission = (props) => {
const [doneMissions, setDoneMissions] = useState([]);
return (
<Card className={classes.users}>
<ul>
{props.missions.map((mission) => (
<li
className={
doneMissions.includes(mission.id)
? classes.done
: ""
}
key={mission.id}
>
{mission.mission1}
<div className={classes.btn2}>
<Button
onClick={() => {
setDoneMissions((prevState) => {
return [...prevState, mission.id];
});
}}
className={classes.btn}
>
Done
</Button>
</div>
</li>
))}
</ul>
</Card>
);
};
export default AddMission;
Hope that helps a bit!
m.isDeleted = true;
m is mutated, so React has no way of knowing that the state has changed.
Pass a function as a prop from the parent component that allows you to update the missions state.
<Button
onClick={() => {
props.deleteMission(mission.id);
}}
className={classes.btn}
>
Done
</Button>;
In the parent component:
const deleteMission = (missionId) => {
setMissions(prevMissions => prevMissions.map(mission => mission.id === missionId ? {...mission, isDeleted: true} : mission))
}
<AddMission missions={mission} deleteMission={deleteMission} />
import React from 'react';
import axios from 'axios';
class App extends React.Component {
state = { advice: '' };
componentDidMount() {
this.fetchAdvice();
}
fetchAdvice = () => {
axios.get('https://api.adviceslip.com/advice')
.then((response) => {
const { advice } = response.data.slip;
this.setState({advice});
})
.catch((error) => {
console.log(error);
})
}
render () {
const { advice } = this.state;
return (
<div className="app">
<div className="card">
<h1 className="heading">{advice}</h1>
<button className="button" onClick={this.fetchAdvice}>
<span>Give Me Advice!</span>
</button>
</div>
</div>
);
}
}
export default App;
According to your comment, you are calling App with
<li> <Link href="#advice"> <NavLink onClick={App}>Get an Advice</NavLink> </Link> </li>
The way that you are calling App is wrong. This binds the App as a function to the event onClick. This is not the way to use a React Class Component or React Router Link.
Provide a to parameter value and map it to App in the Route using the Switch statement.
For reference, checkout this example
https://reactrouter.com/web/example/basic
You can't assign a react component to an onClick event. Or i should say to any events. You can't call components like callback functions. That's wrong. React does not support routing with URL change by itself. You should use a third party library called react-router-dom. Here's a link to get you started with react router. https://reactrouter.com/web/guides/quick-start
I have the below sample code using react-router and context hooks where I am trying to understand why it behaves differently when I use anchors instead of Link components. The Link components are commented out.
This app just simply displays a screen with an html link where you can click it to display component 2 (component 1 is displayed initially). I am updating the context value in the onClick event for the anchor (I use the setName function to update the name in the context).
When I use anchor tags, it doesn't keep the context value that was updated. So when it goes to component2, the name value in the context displays as person1. However, if I comment out the anchors and use the Link components instead, the context value is updated properly.
Why do the Link components work as expected but not the anchors when updating context?
import React, { useContext, useState } from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import { useHistory } from 'react-router-dom';
const NameContext = React.createContext();
function App() {
const [name, setName] = useState('name1');
return (
<NameContext.Provider value={{ name, setName }}>
<Router>
<Route exact path="/" component={Component1} />
<Route exact path="/component1" component={Component1} />
<Route exact path="/component2" component={Component2} />
</Router>
</NameContext.Provider>
);
}
function Component1() {
const { name, setName } = useContext(NameContext);
const history = useHistory();
return (
<>
<div>This is component 1, name = {name}</div>
<a href="/component2" onClick={() => setName('name2')}>
Click to display component 2
</a>
{/* <Link
onClick={() => setName('name2')}
to={(location) => {
return { ...location, pathname: '/component2' };
}}
>
Click to display component 2
</Link> */}
</>
);
}
function Component2() {
const { name, setName } = useContext(NameContext);
const history = useHistory();
return (
<>
<div>This is component 2, name = {name}</div>
<a href="/component1" onClick={() => setName('name3')}>
Click to display component 1
</a>
{/* <Link
onClick={() => setName('name3')}
to={(location) => {
return { ...location, pathname: '/component1' };
}}
>
Click to display component 1
</Link> */}
</>
);
}
export default App;
An anchor tag reloads the browser by default. If you want to avoid this default behavior you can call the preventDefault method on the onClick event.
react-router doesn't use anchor tags either, so if you want to use anchor tags you have to manually update the history.
<div>This is component 1, name = {name}</div>
<a
href="/component2"
onClick={(e) => {
e.preventDefault();
setName("name2");
history.push("/component2");
}}
>
Click to display component 2
</a>
I am on page '/hello'
I have below component in the page '/hello':
<Link to="/world" onClick={() => window.location.reload()}>
...some lines of code
</Link>
Current behaviour: When I click on Link, '/hello' page will refresh, then nothing happens.
Expected behaviour: After clicking, go to '/world' page, then auto-refresh the /world page.
Thank you so much for helping!
I am not sure if this is the right answer,
Why don't you just use link to redirect to the world page and use refresh in useEffect
<Link to="/world">
//code
</Link>
And in the world page
import React,{ useEffect } from "react";
const World = ()=>{
useEffect(() => {
window.location.reload;
}, []);
//...
return(
//...)
}
I recommend you to remove onClick={() => window.location.reload()} from the Link. Then, if you want to reload this world every time the use redirects to this page, inside world component, in the useEffect section define the reload. So, your world component would look something like this:
for functional component:
const World = () => {
useEffect(() => {
window.location.reload()
}, [])
return (
<>
</>
)}
export default World