login not working properly in Login state update not re-rendering - javascript

I'm integrating an axios login API for my app. app.js is the parent component,login.js is the child.
The idea was to declare a state isAuthenticated with false as defaultvalue. and then in the app.js with an if-else render all the components only if isAuthenticated is true. In else the login component will be displayed. isAuthenticated will be set to true after successfull login. but after successfull login login isAuthenticated is updated to true but the if part is not getting rendered. it just shows the login. shouldn't the state update initiate a re-render?
I'm just starting on React. What am I doing wrong guys. Any help would be much appreciated.
on clicking login post api the response is 200 ok. I'm using an if to change the isAuthenticated in the parent app.js. isAuthenticated is set to false by default. all works okay but the if statement in app.js that checks this.state.isAuthenticated is not re-rendering.
this is the main component App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state= {
isAuthenticated:false,
}
}
setAuth=this.setAuth.bind(this);
Logout=this.Logout.bind(this);
setAuth(auth){
console.log('setauth',auth);
this.state.isAuthenticated=true;
console.log('isauth',this.state.isAuthenticated)
//this.forceUpdate()
}
Logout(){
this.setState({isAuthenticated:false});
message.success('Logout Successful');
}
render() {
console.log(this.state.isAuthenticated,"before if");
if(this.state.isAuthenticated) {
return(
<div>
-------------content--------------
</div>
)} return (
<WrappedLogin setAuth={this.setAuth}/>
);
}
}
this is the child component
class Login extends React.Component {
constructor(props) {
super(props);
this.state= {
loginStatus:[],
}
}
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log(' values of form: ',values );
axios.post('http://192.168.137.1:8000/user/login/',values)
.then(res => {
console.log(res);
console.log(res.data.status);
if (res.data.status==200)
{this.props.setAuth(true)
console.log('if');}
else
console.log('password incorrect');})
render() {
return (
);
}
}
const WrappedLogin = Form.create({ name: 'normal_login' })(Login);
export default WrappedLogin;
this is what I'm getting on console
values of form: {outlook_id: "aashik1", password: "Be$t^197901*"}
{data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
200
setauth true
isauth true
if

You shouldn't modify state directly, it will not re-render your component. Instead of
this.state.isAuthenticated = true;
you should write
this.setState({ isAuthenticated: true });
https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly

Related

Child component does not load event of parent component in React

I creating chat application by React.
In the chat application, there is a field for entering user_name and text.
I thought about managing those data with state, I made onNameChange and onTextChange events.
However, in the code I created, onTextChange was loaded but onNameChange was not loaded.
I know that onTextChange in the same file will be loaded.
Even though the files are different, I thought that data can be exchanged via props if the relationship is between parent and child.
I described the code with such a recognition, but I could not get the results I expected.
How can I pass data from LogoutStateForm.js to user_name in ChatForm.js via onNameChange?
ChatForm.js
import React,{Component} from 'react'
import firebase from 'firebase/app'
import { firebaseApp,firebaseDB } from '../firebase/firebase'
import LogoutStateForm from './LogoutStateForm'
const messagesRef = firebaseDB.ref('messages')
class ChatForm extends Component {
constructor(props){
super(props)
this.onNameChange = this.onNameChange.bind(this)
this.onTextChange = this.onTextChange.bind(this)
this.state = {
user: null,
user_name: "",
text: ""
}
}
componentDidMount(){
firebase.auth().onAuthStateChanged(user => {
this.setState({ user })
})
}
onNameChange(e) {
if (e.target.name == 'user_name') {
this.setState({
user_name: e.target.value
}),
console.log(this.state.user_name);
}
}
onTextChange(e) {
if (e.target.name == 'text') {
this.setState({
text: e.target.value
}),
console.log(this.state.text);
}
}
render(){
return(
<div id='Form'>
{this.state.user ?
<LogoutStateForm onClick={this.onNameChange} />:
null
}
//In order to switch display depending on login availability
<textarea name='text' onChange={this.onTextChange} placeholder='メッセージ'/>
</div>
)
}
}
export default ChatForm
LogoutStateForm.js
import React,{Component} from 'react'
import firebase from 'firebase/app'
class LogoutStateForm extends Component {
constructor(props){
super(props)
}
login() {
const provider = new firebase.auth.GoogleAuthProvider()
firebase.auth().signInWithPopup(provider)
}
componentDidMount(){
firebase.auth().onAuthStateChanged(user => {
this.setState({ user })
})
}
render(){
return(
<div className='logout'>
<input name='user_name' onChange={this.props.onNameChange} placeholder='名前'/>
<button onClick={this.login}>Goggle Login</button>
</div>
)
}
}
export default LogoutStateForm
Please lend me your wisdom.
Thank you.
First, in ChatForm.js, what you render LoginStateForm not LogoutStateForm.
Second, assuming it's supposed to be LogoutStateForm, at ChatForm component you pass onNameChange as onClick to LogoutStateForm.
However, you access the props as onNameChange in LogoutStateForm which is wrong. You should access it as the props name that you give, which is this.props.onClick.
Hope it helps.
In ChatForm.js, you are rendering wrong component, It should be LogoutStateForm.
Second you should access prop which you have passed.
ChatForm.js
<LogoutStateForm onNameChange={this.onNameChange} />
In LogoutStateForm.js
render(){
return(
<div className='logout'>
<input name='user_name' onChange={this.props.onNameChange} placeholder='名前'/>
<button onClick={this.login}>Goggle Login</button>
</div>
)
}
Also, define PropTypes in LogoutStateForm.js for verifying type check.
https://reactjs.org/docs/typechecking-with-proptypes.html

Wait for data to be fetched in child components, then render

I have a React app that uses multiple fetch calls throughout different components. In Home page component, I have imported smaller components, all of whom have it's own fetch call.
render() {
return (
<React.Fragment>
<Banner/>
<Services />
<About />
</React.Fragment>
)
}
Banner, Services and About have their own fetch calls to different endpoints, now my question is because the response is a little bit on the slower side, how to wait for all of the child components to fetch data, then render the Homepage component. I have tried to put the state of isLoading and add a loader to wait for components to fetch, but I don't know what to wait for to set isLoading to false.
...how to wait for all of the child components to fetch data, then render the Homepage component
You don't. Instead, you move the fetches to the Homepage component's parent, and then have that parent only render the Homepage component when it has all of the necessary information to do so. In React parlance, this is "lifting state up" (e.g., up the hierarchy to the parent).
While you could render the Homepage in a "loading" form, and have it render its child components in a "loading" form, and have the child components call back to the Home page to say they have their information now, that's more complicated than simply lifting the state up to the highest component that actually needs it (so it knows it can render the Homepage).
As #TJCrowder mentioned in his answer, You'll need to lift your state up and keep it in the parent component. Make a network request there and pass the data to your child component as props. You can read more about lifting-state-up here
class YourComponent extends React.Component {
state = {isLoading: true, isError: false, banner: null, services: null, about: null};
async componentDidMount() {
try {
const [banner, services, about] = await Promise.all([
// all calls
]);
this.setState({ isLoading: false, banner, services, about });
} catch (error) {
this.setState({ isError: true, isLoading: false });
}
}
render() {
if (this.state.isLoading) {
return <div>Loading...</div>
}
return (
<React.Fragment>
<Banner data={this.state.banner} />
<Services data={this.state.services} />
<About data={this.state.about} />
</React.Fragment>
)
}
}
using promises in fetch you could, as suggested, have a isLoaded property state determine whether or not a component should render or not.
class ShouldRender extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
isLoaded: false,
}
}
componentDidMount() {
fetch('http://someresource.com/api/resource')
.then(res => res.json())
.then(data => {
this.state({
data,
isLoaded: true,
});
})
}
render() {
const { isLoaded } = this.state;
if (isLoaded) {
return <MyAwesomeReactComponent />
}
return null;
}
}
So once the state is updated it will trigger a rerendering of the component with the new state that will render the if statement true and you're JSX will appear.

Need to Execute Function before render() in ReactJS

I've created a login system with React which stores a session when the user logs in. When the page is reloaded, I have added a function which should check if the session exists and then either setState() to true or to false.
As I'm new to React, I'm not sure how to execute this function. Please see my code below for App.js:
import React from 'react';
import './css/App.css';
import LoginForm from "./LoginForm";
import Dashboard from "./Dashboard";
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
renderLoginForm: true
};
this.handleLoginFormMount = this.handleLoginFormMount.bind(this);
}
handleLoginFormMount() {
this.setState({
renderLoginForm: false
});
}
// Check session function.
checkSession() {
fetch('/check-session', {
credentials: 'include'
})
.then((response) => {
return response.json();
})
.then((sessionResult) => {
if (sessionResult.username) {
console.log('false');
this.setState({
renderLoginForm: false
});
} else {
console.log('true');
this.setState({
renderLoginForm: true
});
}
})
.catch((error) => {
console.log('Error: ', error);
});
}
render() {
checkSession();
return (
<div className="App">
{this.state.renderLoginForm ? <LoginForm mountLoginForm={this.handleLoginFormMount} /> : null}
{this.state.renderLoginForm ? null : <Dashboard />}
</div>
);
}
}
export default App;
Having checkSession() in this position outputs the following in the console when loading the page:
Line 50: 'checkSession' is not defined no-undef
If I put the function outside of the class App extends React.Component {}, then it tells me that I cannot set the state of undefined.
Functional Component: In my case I wanted my code to run before component renders on the screen. useLayoutEffect is a hook provided by React for this exact purpose.
import React, { useLayoutEffect } from "react";
...
const App = () => {
useLayoutEffect(() => {
//check local token or something
}, []);
}
Read More: https://reactjs.org/docs/hooks-reference.html#uselayouteffect
Having checkSession() in this position outputs the following in the console when loading the page:
Line 50: 'checkSession' is not defined no-undef
That's because it's a method, but you're calling it like a freestanding function. The call should be this.checkSession();. But keep reading.
Separately:
The render function must be pure, it cannot have side-effects like changing state. Instead, put any side-effects code in componentDidMount; from the documentation for that lifecycle method:
If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
Be sure that your component renders correctly for the original state (before the session check), as well as for the updated state (after the session check).
More about lifecycle methods and such in the documentation.
Alternately, if this component can't do anything useful without the session, you might move the session check to its parent component, and have the parent only render this child component when it has the session check results.

React Nav Bar Not Rerendering With New State

I'm making a nav bar in react that shows different links based on whether or not the user is logged in. (We use firebase for account stuff.)
Here's what I have:
async function isLoggedIn() {
return await UsersManagement.isLoggedIn();
}
export default class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = ({ loggedIn: isLoggedIn() });
}
componentDidMount() {
this.setState({ loggedIn: isLoggedIn()})
}
render() {
return (
{this.state.loggedIn ? (
<LoggedInLinks />
) : (
<NotLoggedInLinks />
)}
}
}
I tried removing state from this component, using the function UserManagement.isLoggedIn() directly. That function works. I can see through print statements that it returns the proper userid or null when not logged in.
So what am I missing? Is there a better way to create a nav bar in react that changes based on logged in status?
Thank you!

React state/props won't update

I have the code below, but my state won't update.
I am using Angular http ajax-call to receive if the user is correct or not. When I pass the the new error-message as a prop nothing happens, but the component does receive it since I can access it through nextProps.
I have also tried to skip the constructor, componentWillReceiveProps and shouldComponentUpdate to just render out { this.props.error }, but that did not work either.
This is my render-function to render the DOM first time
// Some code
.then(function(response){
// Some code
}, function(response){
_this.renderLogin("User not found"); // Is sending the error-message to the function
});
// Some code
_this.renderLogin = function(error){
render(
<Login error={error} />,
document.getElementById("app")
);
};
_this.renderLogin("Standard");
This is the Login-component:
class Login extends React.Component {
constructor(props){
super(props);
this.state = {
error: this.props.error
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.error !== this.state.error) {
this.setState({ error: nextProps.error });
console.log(nextProps.error); // User not found
console.log(this.state.error); // Standard
}else{}
}
shouldComponentUpdate(nextProps, nextState){
console.log(nextState.error); // User not found
console.log(nextProps.error); // User not found
console.log(this.state.error); // Standard
return true;
}
render(){
return(
<div>
{ this.state.error } // Always showing: 'Standard'
</div>
);
}
}
export default Login;
Thanks in advance for any help!
From what I can see of your code Login should not be a stateful component since it does nothing to mutate the state... its just setting a prop it receives to its state for no reason. In React state is passed down with props and renders are triggered on components that need updating with the new prop value. Nothing is happening in your code because the component has already been affixed to the DOM, but your're trying to reaffix it to the DOM with a new value with this
.then(function(response){
// Some code
}, function(response){
_this.renderLogin("User not found"); // Is sending the error-message to the function
});
Something like that code needs to be within stateful react component that evaluates if the user is logged in or not. The state must be mutated WITHIN a react component and not outside trying to pass it in. In the code below I didn't change your Login to be stateless, but it still works because I've muted the value within a React component.
class RenderLogin extends React.Component {
constructor(props){
super(props);
this.state = {
errors: "Standard",
};
this.changeError = this.changeError.bind(this);
}
changeError() {
this.setState({errors:"Boom"});
}
render() {
return (
<div>
<Login error={this.state.errors} />
<button onClick={this.changeError}>Change</button>
</div>
);
}
}
class Login extends React.Component {
constructor(props){
super(props);
this.state = {
error: this.props.error
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.error !== this.state.error) {
this.setState({ error: nextProps.error });
console.log(nextProps.error); // User not found
console.log(this.state.error); // Standard
}else{}
}
shouldComponentUpdate(nextProps, nextState){
console.log(nextState.error); // User not found
console.log(nextProps.error); // User not found
console.log(this.state.error); // Standard
return true;
}
render(){
return(
<div>
{ this.state.error } // Always showing: 'Standard'
</div>
);
}
}
ReactDOM.render(<RenderLogin />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<!-- begin snippet: js hide: false console: true babel: true -->
<div id="app"></div>

Categories

Resources