Apollo 2.1, Mutation and React-Router - reactjs

I am pretty new to Apollo and starting right with version 2.1.
I also use react-router but I don't know how to do a browserHistory.push after a mutation was completed. Here are parts of my code:
index.js
const client = new ApolloClient({
uri: "http://localhost:4000/graphql"
});
ReactDOM.render(
<ApolloProvider client={client}>
<AppContainer>
<BrowserRouter basename={basename}>
<I18nextProvider i18n={i18n}>
<Layout />
</I18nextProvider>
</BrowserRouter>
</AppContainer>
</ApolloProvider>,
document.getElementById('react_root')
)
};
In onCompleted I want to show a different page to tell user to check the emails. But I don't know how to do so.
RegistrationForm.js
import {browserHistory) from 'react-router';
const onCompleted = (data) => {
browserHistory.push("/de/register/check-email");
}
const RegistrationForm = () => {
return (
<Mutation mutation={REGISTER_USER}
onCompleted={onCompleted}
>
{(register, { data }) => (
<div>
<Form
onSubmit={onSubmit}
render={({ handleSubmit, pristine, invalid, values, variables }) => (
<form onSubmit={(e, ) => {
e.preventDefault();
register({
variables: {
input: {
username: values.username,
email: values.email,
password: values.password
}
}
});
}}
>
<div>
<label>Username</label>
<Field name="username" component="input" placeholder="Username" />
</div>
<div>
<label>Email</label>
<Field name="email" component="input" placeholder="Email" />
</div>
<div>
<label>Password</label>
<Field name="password" component="input" placeholder="Password" />
</div>
<button type="submit" >Submit</button>
</form>
)}
/>
</div>
)}
</Mutation>
);
};
Does anybody knows how to do so? Thanks a lot for your help.
Best regards

There's no export browserHistory in react-router, but there is a history prop.
If your component is immediately under a <Route>, you can use it like this:
const RegistrationForm = ({history}) => {
const onCompleted = (data) => {
history.push("/de/register/check-email");
}
return (
..
If your component is deeper in the tree, you can inject the history and other route props with withRouter, e.g.:
const RegistrationFormWrapped = withRouter(RegistrationForm);
or
export default withRouter(RegistrationForm);
And because onCompleted depends on a prop now and needs to be local, it makes sense to convert RegistrationForm to a class.

I think I found the solution. I passed history as a prop from the calling component and then went with:
const RegistrationFinalForm = ({ history }) => {
return (
<Mutation mutation={REGISTER_USER}
onCompleted={(data) => {
history.push("/de/register/check-email");
}}>

Related

How to navigate to another page in reactJS using SubmitHandler?

I wan to navigate to the dashboard page immediately after I submit the details. Here this is my CreateMonthWallet class where I am creating different for different months. Inside this create form all the information are there which are needed to be collected by the user. Once the user click the create button, the user should navigate back to the Dashboard page. Below given is the code of CreateMonthWallet class. When I run the code, once after clicking the cleate button it gives me the message of error but the data is updated to database but still its showing the message of error on the localhost page and doesn't navigating to the dashboard.
import axios from 'axios'
import React, { Component } from 'react'
class CreateMonthWallet extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
accountNumber: '',
description: '',
priority: ''
}
}
changeHandler = (event, fieldName) => {
this.setState({
[fieldName]: event.target.value
})
}
submitHandler = (event) => {
const newWallet = {
name: this.state.name,
accountNumber: this.state.accountNumber,
description: this.state.description,
priority: this.state.priority
}
axios.post('http://localhost:8080/monthwallet', newWallet)
.then((res) => {
this.props.history.push('/Dashboard')
}).catch((err) => {
alert("Error")
})
event.preventDefault()
}
render() {
return (
<div className="project">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h5 className="display-4 text-center">Create Wallet</h5>
<hr />
<form onSubmit={(event)=>this.submitHandler(event)}>
<div className="form-group">
<input type="text" onChange={(event) => this.changeHandler(event, "name")} className="form-control form-control-lg " placeholder="Account Name" />
</div>
<div className="form-group">
<input type="text" onChange={(event) => this.changeHandler(event, "accountNumber")} className="form-control form-control-lg" placeholder="Account No" />
</div>
<div className="form-group">
<textarea onChange={(event) => this.changeHandler(event, "description")} className="form-control form-control-lg" placeholder="Description"></textarea>
</div>
<div className="form-group">
<select className="form-control form-control-lg" onChange={(event) => this.changeHandler(event, "priority")}>
<option value={3}>Display Priority</option>
<option value={1}>High</option>
<option value={2}>Medium</option>
<option value={3}>Low</option>
</select>
</div>
<input type="submit" className="btn btn-dark btn-block mt-4" value="Create" />
</form>
</div>
</div>
</div>
</div>
)
}
}
export default CreateMonthWallet
Edit:
Adding the content of function App to the question. As it became relevant.
function App() {
return (
<Router>
<div>
<Nav />
<Routes>
<Route path="/" element={<Welcome />} exact />
<Route path="/Dashboard" element={<Dashboard />} exact />
<Route path="/CreateMonthWallet" element={<CreateMonthWallet />} exact />
<Route path="*" element={<NotFound />} exact />
</Routes>
</div>
</Router>
)
}
Issue
react-router-dom#6 removed the route props, so there should be some sort of error in the console along the lines of can't access property "push" of undefined when trying to execute this.props.history.push('/Dashboard'). The history object was replaced by a navigate function in RRDv6, accessible via the useNavigate hook.
Solution
You can either convert CreateMonthWallet into a React function component so it can use React hooks, or you can create a custom withRouter Higher Order component to inject the navigate function as a prop.
Convert to React function component
import axios from 'axios'
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
const CreateMonthWallet = () => {
const navigate = useNavigate();
const [state, setState] = useState({
name: '',
accountNumber: '',
description: '',
priority: ''
});
const changeHandler = (event) => {
const { name, value } = event.target;
setState(state => ({
...state,
[name]: value
}))
}
const submitHandler = (event) => {
event.preventDefault();
axios.post('http://localhost:8080/monthwallet', state)
.then((res) => {
navigate('/Dashboard');
})
.catch((err) => {
alert("Error");
});
}
return (
<div className="project">
<div className="container">
<div className="row">
<div className="col-md-8 m-auto">
<h5 className="display-4 text-center">Create Wallet</h5>
<hr />
<form onSubmit={submitHandler}>
<div className="form-group">
<input
type="text"
name="name"
onChange={changeHandler}
className="form-control form-control-lg"
placeholder="Account Name"
value={state.name}
/>
</div>
<div className="form-group">
<input
type="text"
name="accountNumber"
onChange={changeHandler}
className="form-control form-control-lg"
placeholder="Account No"
value={state.accountNumber}
/>
</div>
<div className="form-group">
<textarea
name="description"
onChange={changeHandler}
className="form-control form-control-lg"
placeholder="Description"
value={state.description}
/>
</div>
<div className="form-group">
<select
className="form-control form-control-lg"
name="priority"
onChange={changeHandler}
value={state.priority}
>
<option value={3}>Display Priority</option>
<option value={1}>High</option>
<option value={2}>Medium</option>
<option value={3}>Low</option>
</select>
</div>
<input
type="submit"
className="btn btn-dark btn-block mt-4" value="Create"
/>
</form>
</div>
</div>
</div>
</div>
);
};
export default CreateMonthWallet;
Create withRouter HOC
Follow the instructions at What happened to withRouter? I need it! part of the RRDv6 FAQ to create a replacement withRouter HOC.
Example:
import {
useLocation,
useNavigate,
useParams,
} from "react-router-dom";
const withRouter = Component => props => {
const location = useLocation();
const navigate = useNavigate();
const params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
Decorate the CreateMonthWallet with the new withRouter HOC and access this.props.router.navigate.
class CreateMonthWallet extends Component {
...
submitHandler = (event) => {
event.preventDefault();
const newWallet = { ...this.state };
axios.post('http://localhost:8080/monthwallet', newWallet)
.then((res) => {
this.props.router.navigate('/Dashboard');
})
.catch((err) => {
alert("Error")
});
}
render() {
return (
...
);
}
}
export default withRouter(CreateMonthWallet);
Looks like you did not create a history variable yet. It's a piece of cake.
This variable that you are going to create, named history, will be responsible for storing the history of your browsing within your app.
With history you earn the ability to goBack(), for example. Notice that react-router is now favoring navigate instead. You can have a look later at the link below later for upgrading to newer versions.
react-router go back a page how do you configure history?
But let's keep ourselves in the problem here.
import {BrowserRouter, } from "react-router-dom";
import { createBrowserHistory } from 'history';
function App() {
const history = createBrowserHistory() //creates the variable history
return (
<BrowserRouter>
<div>
<Nav />
<Routes>
<Route path="/" element={<Welcome />} exact />
<Route path="/dashboard" element={<Dashboard history={history}/>} exact /> //passes it down to child component
<Route path="/createmonthwallet" element={<CreateMonthWallet history={history}/>} exact /> //passes it down to child component
<Route path="*" element={<NotFound />} exact />
</Routes>
</div>
</BrowserRouter>
)
}
What we did here:
Create a history object by importing createBrowserHistory from history. Use 'npm install history' if you do not have it yet.
Defining the variable inside your App function.
Pass it down as 'props' in your Route.
Now, your component will have access to this.props.history and you the error should go away.
As sidenotes, prefer using function components anywere in your code. Much easier to read, write, and much more flexible. Also, avoid using uppercase letters in path. something like path="wallet/month/create" would look more elegant in the long term cause you could have "wallet/month/update", "wallet/month/delete" and so on

handleChange is not a function , react

I am trying to pass a function to a react component but it say that it is not a function. I can console log the props and I have set it up, as far as i can tell, the same way I have always done. Please help, thanks!
EDIT: I get the same error when i try to test the onSubmit function, too
<div className="bookshelf">
<Searchbar
handleChange={onChange}
addInputClass={"searchbar_mobile_input"}
addBtnClass={"searchbar_mobile_btn"}
/>
<div className="bookshelf_heading_container">
<h2 className="bookshelf__heading">
Release the Kraken of Knowledge!
</h2>
</div>
import React, {Fragment} from "react";
const SearchBar = ({ addBtnClass, addInputClass, handleChange }) => {
console.log(handleChange)
return (
<Fragment>
<form className="search__form__input" onSubmit={() => handleSubmit(e)}>
<input
type="text"
placeholder="Search by Title/Author"
className={`searchbar__input ${addInputClass}`}
name="search"
onChange={(e) => handleChange(e)}
></input>
<button type="submit" className={`btn ${addBtnClass}`}>
Search
</button>
</form>
</Fragment>
);
};
export default SearchBar;
Here is an example that should help you. You should be able to take this simplified example and work out how to apply it to your example. I would've used your code but it was incomplete and no sandbox was provided.
import React from "react";
export default function AppLogic() {
const [readValue, writeValue] = React.useState("");
const handleChange = (e) => writeValue(e.target.value);
const handleSubmit = (e) => {
e.preventDefault();
alert("Submitted");
};
const props = { readValue, handleChange, handleSubmit };
return <AppView {...props} />;
}
function AppView({ handleSubmit, handleChange, readValue }) {
return (
<div className="App">
<h1>Name</h1>
<form onSubmit={handleSubmit}>
<input onChange={handleChange} value={readValue} />
<input type="submit" />
</form>
<p>{readValue}</p>
</div>
);
}

How to redirect and pass data as props on form submit in React?

I have a functional component as so:
const SearchForm = () => {
//stuffs
const handleSubmit = e => {
e.preventDefault();
fetchData();
return <Redirect to='/search' data={fetchedData} />
}
return (
<div>
<form onSubmit={ handleSubmit }>
<div className='input-field'>
<input placeholder="Search whatever you wish"
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
className='searchfield center-align white-text'
/>
</div>
</form>
</div>
)
}
export default SearchForm;
After the form is submitted, I want the page to be redirected to /search, and I want the fetchedData to be passed along with it. I tried using the Redirect component from react-router-dom but it doesn't seem to work. Any fix?
you can do from two ways
1.
const SearchForm = () => {
//stuffs
const [redirect, setRedirect] = useState(false);
const [data, setData] = useState();
const handleSubmit = e => {
e.preventDefault();
fetchData();
setRedirect(true);
setData(e);
}
if (redirect)
return <Redirect to={{ pathname: '/search', data: { data } }} />
return (
<div>
<form onSubmit={handleSubmit}>
<div className='input-field'>
<input placeholder="Search whatever you wish"
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
className='searchfield center-align white-text'
/>
</div>
</form>
</div>
)
}
export default SearchForm;
2.
const SearchForm = (props) => {
//stuffs
const [redirect, setRedirect] = useState(false);
const [data, setData] = useState();
const handleSubmit = e => {
e.preventDefault();
fetchData();
props.history.push({
pathname: '/search',
state:
{
//your data
}
})
}
return (
<div>
<form onSubmit={handleSubmit}>
<div className='input-field'>
<input placeholder="Search whatever you wish"
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
className='searchfield center-align white-text'
/>
</div>
</form>
</div>
)
}
export default SearchForm;
this is how to do it by passing state object in to prop:
<Redirect
to={{
pathname: "/search",
state: { data:fetchedData}
}}
/>
You can not use the <Redirect /> component in your fallback function because it's a component and can not be rendered there.
If you are using react-router-dom, you can easily use its hooks to redirect the user to another route. Using hooks are simpler and keeps your components easy to read.
Try this:
import { useHistory } from "react-router-dom";
const SearchForm = () => {
const { push } = useHistory();
//stuffs
const handleSubmit = e => {
e.preventDefault();
fetchData();
push({
pathname: '/search',
state: {
data: fetchedData
}
})
}
return (
<div>
<form onSubmit={handleSubmit}>
<div className='input-field'>
<input placeholder="Search whatever you wish"
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
className='searchfield center-align white-text'
/>
</div>
</form>
</div>
)
}

React-cookies with hooks

i'm using React-cookies, redux, redux-thunk and hooks.
I don't understand how to store the value of " token " as a cookie.
this is the Component App.js
<Provider store={store}>
<CookiesProvider>
<BrowserRouter>
<Switch>
<Route exact path="/release/:id" component={Release} render={() => (<Login cookies={this.props.cookies} />)} />
<Route exact path="/login" render={() => (<Login cookies={this.props.cookies} />)} component={Login} />
</Switch>
</BrowserRouter>
</CookiesProvider>
</Provider>
actually the Login component is made as hook
I receive the value of the token because of call made with this component
function Form({ handleSubmit, login }, props) {
const [token, setToken] = useState(undefined);
const onSubmit = (user) => {
login(user);
};
return (
<form onSubmit={handleSubmit(onSubmit)} className={styles.flexColumn}>
<div className={styles.username}>
<P>username</P>
<Field name="username" component="input" type="text" className={styles.input} />
</div>
<div className={styles.password}>
<P>password</P>
<Field
name="password"
component="input"
type="text"
className={styles.input}
/>
</div>
<div className={styles.downSection}>
<Flex>
<div>
<P>
Serve Aiuto?
</P>
</div>
<a href="#">
<div className={styles.contactLink}>
<P>Contattaci</P>
</div>
</a>
</Flex>
<Button type="submit" text="Accedi" />
</div>
</form>
);
}
Form.propTypes = {
handleSubmit: PropTypes.func.isRequired,
login: PropTypes.func.isRequired,
};
const mapStateToProps = (state, ownProps) => ({
cookies: ownProps.cookies,
}, console.log(ownProps));
const mapDispatchToProps = {
login: loginAction,
};
const enhance = compose(
connect(mapStateToProps, mapDispatchToProps),
reduxForm({ form: 'login' }),
);
export default enhance(Form);
How could i store the value token as a cookie ? i got this value thanks to loginAction
I must use the library react-cookies.
Thanks.
Use a library for it...
Such as react-cookie.
You can simply do:
import {useCookie} from 'react-cookie'
And use cookies.
Take a look here

React formik read input value outside <Formik/> in real time onchange

Here is the code https://codesandbox.io/s/5wmrp396kp
I have no idea how to read the value outside the .
I just need a very simple function to show the value in other component like the helper.js
class ComponentOne extends Component {
render() {
return (
<React.Fragment>
<div>CompoenntOne: I wannna read the props.values.email here</div>
<div>CompoenntOne: I wannna read the props.values.email2 here</div>
</React.Fragment>
);
}
}
Any way to made this work in simple?
Your code doesn't even have Formik components included.
Please, implement something like this basic example from docs:
import React from 'react';
import { Formik } from 'formik';
const BasicExample = () => (
<div>
<h1>My Form</h1>
<Formik
initialValues={{ name: 'jared' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={props => (
<form onSubmit={props.handleSubmit}>
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.name}
name="name"
/>
{props.errors.name && <div id="feedback">{props.errors.name}</div>}
<button type="submit">Submit</button>
</form>
)}
/>
</div>
);

Resources