I'm learning React and TypeScript and I am trying to write a login form, but after checking the user's data and creating the cookie, I want to rerender the parent component.
I have index.tsx (short version):
import React from 'react';
import ReactDOM from 'react-dom';
import cookie from 'react-cookies'
function Main() {
let hmCookies = cookie.loadAll();
console.log(hmCookies.auth);
if (hmCookies.auth === 'true') {
return (<Logout />)
} else {
return (<Login />)
}
}
ReactDOM.render(<Main />, document.getElementById('root'));
and Logint.tsx:
import React from 'react';
import cookie from 'react-cookies'
const axios = require('axios');
class Login extends React.Component<any, any> {
...
handleSubmit(event) {
axios.post('http://localhost/php/Login.php', {
login: this.state.login,
password: this.state.password
}, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}})
.then(function (response) {
if (response.data.auth == true) {
cookie.save('auth', true);
}
})
.catch(function (error) {
console.log(error);
});
event.preventDefault();
}
render() { return ( <LoginFormHere /> ) }
}
export default Login;
After posting the user data in the form and making a request to PHP script via ajax, PHP returns a response. If it's true, save cookie. So, after changing cookie, I want to rerender component Main. However, I have no idea how to do this and I can't find any examples of this in the documentation.
How can this be achieved?
You can create a function in your Main component to serve to rerender. Let's call it - updateParent. Then you can pass updateParent function to your Login component as props and use it if response.data.auth == true. updateParent can be implemented as in the following question.
Here the example.
Also, you could use a stateful(class) component instead of functional. This allows you to use forceUpdate
Related
I have a very unique issue where I need to render components with different ids on a site, and cannot render all the content under one ID.
I have been able to collect a JSON array through a GET request and have been able to get a for each loop for each array, however I just need to render that particular data with an ID passed from the array.
I have tried to use ReactDom.render in a class but I cannot find how this can be done & and I have current set document.getElementById('modules') to one particular div to begin with to see if that would render, but having no luck.
Any help would be appreciated.
Index.js
`
import { ColorModeScript } from '#chakra-ui/react';
import React, { StrictMode } from 'react';
import { Component } from 'react';
import { ChakraProvider } from "#chakra-ui/react";
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
import * as serviceWorker from './serviceWorker';
import Theme from "./theme";
import Page from "./structure/Page";
import axios from "axios";
class Index extends Component {
constructor(props) {
super(props);
this.state = {
modules: [],
};
}
componentDidMount() {
var currentPath = window.location.pathname;
if (currentPath === '/') {
currentPath = '/home';
}
axios
.get("/lite/modules" + currentPath)
.then((response) => {
const data = response.data;
this.setState({ modules: data });
Object.entries(data).forEach(data1 => {
var ModuleData = data1[1];
this.renderModule(ModuleData);
});
})
.catch((error) => {
console.log(error);
});
}
renderModule(ModuleData){
console.log(ModuleData);
var divID = "module" + ModuleData.type;
ReactDOM.render(
<h1>demo</h1>,
document.getElementById('modules')
);
}
render() {
return (
<ChakraProvider theme={Theme}>
<Page modules={this.state.modules} />
</ChakraProvider>
);
}
}
export default Index;
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorker.unregister();
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more:
reportWebVitals();
`
Render multiple react components with different IDs.
I think you need to add this to your constructor:
this.renderModule = this.renderModule.bind(this)
if you have another server for the DB:
you need to do that when using axios:
axios.get(`${process.env.REACT_APP_SERVER_URL || '**LOCAL_SERVER_URL**'}/...`)
I'm doing a register form in React using a class and I would like to send the user to another page when the register is correct, the problem is that I haven't find any functional way to make it.
.then(data => {
if(data.error != null) {
console.log("Error => "+data.error);
} else {
//Send the user to the login page
}
})
This is the part where I would send him to the login page, I have proved with 'useNavigation' and some more, but I can't find the correct way to get it.
Since react-router-dom v6 supports only hooks.
For implementing the navigate functionality inside a class-based component, you have to create a HOC and pass the hooks as props.
// withRouter.js
import { useNavigate } from 'react-router-dom';
export const withRouter = (Component) => {
const Wrapper = (props) => {
const navigate = useNavigate();
// any other hooks can be added and passed on
return (
<Component
navigate={navigate}
{...props}
/>
);
};
return Wrapper;
};
then on your RegisterForm.jsx you can
import {withRouter} from './withRouter';
class RegisterForm extends React.Component{
//...
yourFunction =() =>{
.then(data => {
if(data.error != null) {
console.log("Error => "+data.error);
} else {
//Send the user to the login page
this.props.navigate('/loginPage');
}
})
}
//...
}
export default withRouter(RegisterForm);
(https://github.com/remix-run/react-router/issues/7256)
I am returning a custom redirect URL for OAuth using a Nodejs back end. I am trying to get React to redirect the user to this URL however it's adding localhost to the front of it.
import React, { Component } from "react";
import { withRouter } from "react-router";
import axios from "axios";
class Test extends Component {
async componentDidMount() {
const profileData = await axios.get("/connect");
this.props.history.push(profileData.data);
}
render() {
return <div />;
}
}
export default withRouter(Test);
So let's say the profileData.data returned string is http://www.google.com, the webpage gets redirected to http://localhost:3000/http://www.google.com.
Is there a way of redirecting with React to make it redirect to a specific URL rather than it trying to redirect to a sub page?
Simply put a set the location href inside your componentDidMount function. I would write it like this:
componentDidMount() {
axios.get("/connect")
.then((response) => {
window.location.href = response.data
})
}
I am using a boilerplate called trufflebox's react-auth where
getWeb is called on loading the page (Link to code)
which creates the web3 object (Link to Code)
and stores web3 in the Redux store (Link to code)
Problem: When I retrieve the web3 object from the Redux store, it is undefined, most likely because web3 has not been created yet in Step 2 described above.
What should be the correct way to retrieve web3 from the Redux store only after it has been set?
layouts/test/Test.js
import React, { Component } from 'react';
import store from '../../store';
class Test extends Component {
componentWillMount() {
this.setState({
web3: store.getState().results.payload.web3Instance
})
this.instantiateContract()
}
instantiateContract() {
console.log(this.state.web3) // UNDEFINED!!
}
render() {
return (
<h1>Test</h1>
)
}
}
export default Test
Everything works if I retrieve web3 again without going to the Redux store:
import React, { Component } from 'react';
import getWeb3 from '../../util/web3/getWeb3';
class Test extends Component {
componentWillMount() {
getWeb3
.then(results => {
this.setState({
web3: results.payload.web3Instance
})
this.instantiateContract()
})
}
instantiateContract() {
console.log(this.state.web3)
}
render() {
return (
<h1>Test</h1>
)
}
}
export default Test
Resolve the promise just after creating the store
src/store.js
import getWeb3 from './util/web3/getWeb3';
import { createStore } from 'redux';
//... prepare middlewares and other stuffs , then : create store
const store = createStore(/*....configure it as you want..*/);
// Just after creating store, here the engineering:
getWeb3.then(results => {
// Assuming you have suitable reducer
// Assuming the reducer put the payload in state and accessible via "getState().results.payload.web3Instance"
store.dispatch({ type: 'SAVE_WEB3', payload: results.payload.web3Instance });
});
export default store;
In you ./index.js (where you are rendering the whole app) consider to use Provider component as wrapper to pass store behind the seen and have a singleton store.
src/index.js
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<Test />
</Provider>
)
Now, in your component, connect HOC will do everything , see comments below :
src/.../Test.js
import { connect } from 'react-redux';
class Test extends Component {
// No need any lifecyle method , "connect" will do everything :)
render() {
console.log(this.props.web3)
return (
<h1>Test</h1>
)
}
}
// Retrieve from Redux STATE and refresh PROPS of component
function mapStateToProps(state) {
return {
web3: state.results.payload.web3Instance // since you are using "getState().results.payload.web3Instance"
}
}
export default connect(mapStateToProps)(Test); // the awesome "connect" will refresh props
Maybe try calling instantiateContract during the componentWillReceiveProps phase. Check out the following...
componentWillMount() {
this.setState({
web3: store.getState().results.payload.web3Instance
});
}
instantiateContract() {
console.log(this.state.web3); // hopefully not undefined
}
componentWillReceiveProps(nextProps) {
if(nextProps.whatever) {
this.instantiateContract();
}
}
render() {
return (
<h1>Test</h1>
)
}
where nextProps.whatever is what you are mapping from redux (not totally sure what this is given your details). Ideally this is getting fed back into your component and when the value either populates or changes, you then call your function
Also, I see a lot of state management here opposed to what I would expect to see done via props. if componentWillReceiveProps(nextProps) is not a good hook given your application architecture, componentDidUpdate(prevProps, prevState) could be a viable alternative.
I'm working on a login form in a project with React, Redux and Redux-Thunk. Using Redux-Thunk, I'm able to dispatch async actions like delivering the submitted login form to the back-end and bringing back validated data back to the state via reducers. Once the component gets the data it needs, it can then redirect to the page it needs without a problem.
The issue is, right before redirecting the user I need to write some data which came from asynchronous network request to the localStorage. If I don't do this async, the user gets redirected with the initial state values written to the local storage.
As a solution, I'm using promises and timeouts right in the React component to wait for the incoming data.
This approach seems to work but it doesn't feel right, can someone suggest me a better practice?
Here's the code in the component, I filtered most of the irrelevant things to make it as short as possible here.
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {browserHistory} from 'react-router';
import {reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import {validate} from './../utils/login/surfaceValidation';
import {inputAlerts} from './../utils/login/inputAlerts';
import {submitLogin} from './../redux/actions/index';
class Form extends Component {
componentWillReceiveProps(nextProps) {
if(nextProps.loginApproved) {
this.handleValidLogin();
}
}
handleLogin(props) {
this.props.submitLogin(props);
// submitLogin is async action handled by redux-thunk it returns
// this.props.loginApproved and if it's true componentWillReceiveProps
// will trigger.
}
handleValidLogin() {
this.writeToStorage()
.then(() => {
browserHistory.push('/main');
}).catch((err) => {
console.log(err);
});
}
writeToStorage(){
return new Promise((resolve, reject) => {
setTimeout(() =>{
localStorage.setItem('user',
JSON.stringify({
authenticated: true,
username: this.props.username,
id: this.props.id,
token: this.props.token
}));
}, 3000);
setTimeout(() => {
if(this.props.token != null) {
resolve();
console.log('got a token - resolving.');
} else {
reject();
console.log('no token - rejecting. ');
}
}, 4000);
});
}
render() {
return (
// Login form component is here.
// When user submits form, this.handleLogin() is called.
);
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({submitLogin});
}
function mapStateToProps(state) {
return {
loginApproved: state.login.loginApproved,
username: state.login.username,
id: state.login.id,
token: state.login.token
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Form);
As far as I know localStorage.seItem is synchronous so you can call function saving data to storage before redirecting.