How to manage the state using Redux - reactjs

I'm working on a project, I'm using Django as backend, React as frontend and Redux as state manager. I want to pass a function from my login and register component to my Header component, whereas my header component is not a root component I can't pass params directly to other components. I know redux can solve this problem but I don't know how?
I have two separate links in my header component (Login, Register), I mean when I click on login it should call the openLoginModal() from my login component and the same process for register component
My header component
import React, { Component, Fragment } from "react";
import { Link } from "react-router-dom";
export class Header extends Component {
render() {
return (
<Fragment>
<nav className="navbar navbar-expand-sm ">
<div className="container">
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarTogglerDemo01"
aria-controls="navbarTogglerDemo01"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarTogglerDemo01">
<a className="navbar-brand" href="/">
Todos
</a>
<ul className="navbar-nav ml-auto mt-2 mt-lg-0">
<Link to="/register">
<button className="btn btn-outline-primary btn-sm mx-2">
Register
</button>
</Link>
<Link to="/login">
<button className="btn btn-outline-primary btn-sm">
Login
</button>
</Link>
</ul>
</div>
</div>
</nav>
</Fragment>
);
}
}
export default Header;
I also used react-awesome-modal for creating login and register modal, here is my login component
import React, { Component, Fragment } from "react";
export class Login extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
username: "",
password: "",
};
}
openLoginModal() {
this.setState({
visible: true,
});
}
closeLoginModal() {
this.setState({
visible: false,
});
}
render() {
const { username, password } = this.state;
return (
<Fragment>
<section>
<Modal
visible={this.state.visible}
width="400"
height="300"
effect="fadeInUp"
onClickAway={() => this.closeLoginModal()}
>
<div>
<form>
<div className="form-group">
<label>Username</label>
<input
type="text"
className="form-control"
name="username"
value={username}
/>
</div>
<div className="form-group">
<label>Password</label>
<input
type="password"
className="form-control"
name="password"
value={password}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-sm my-2">Login</button>
</div>
</form>
</div>
</Modal>
</section>
</Fragment>
);
}
}
export default Login;

Just only store variable isOpenModal into the state of redux and add 2 action creator
openModal() {
return { type: 'OPEN_MODAL' }
}
closeModal {
return { type: 'CLOSE_MODAL'
}
change state through reducers (file reducers)
// another code
switch(action.type) {
case 'OPEN_MODAL':
state.isOpenModal = true;
break;
case 'CLOSE_MODAL':
state.isOpenModal = false;
break;
// another code
}
In file Header use mapDispatchToProps to call 2 action creator openModal and closeModal as created above.
In file Login use mapStateToProps to use state isOpenModal in state of redux.

Related

ReactJS: Cannot read property 'bind' of undefined

I'm tried to figure out How can I have these two buttons the Login and Sign up that will be toggling the Login and Sign up box back in forward.
I couldn't figure out.... Does anyone can solve this?
this is my code. I'd like to call "selected-controller" method from div item. But then, Cannot read property 'bind' of undefined.
Navigation.js
import React from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import "../components/pages/Forms/MainScreen";
import Dropdown from "../components//pages/dropdowns/dropdowns";
import hamburger from "../images/menu.svg";
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
isExpanded: false
};
}
handleToggle(e) {
e.preventDefault();
this.setState(prevState => ({
isExpanded: !prevState.isExpanded, // negate the previous expanded state
}));
}
render() {
const { isExpanded } = this.state;
return (
<Router>
<div className="NavbarContainer">
<div className="mobilecontainer LeftNav">
<h2 className="BrandName LeftNav mobileboxmenu inline FarRight">Kommonplaces</h2>
<div className="hamburger inlinev" >
<img
onClick={e => this.handleToggle(e)}
alt="menubtn"
src={hamburger}
/>
</div>
</div>
<ul className={`NavBar collapsed ${isExpanded ? "is-expanded" : ""}`}>
<Dropdown/>
<li className="RightNav"><Link to="/">Host Your Space</Link></li>
<li className="RightNav"><Link to="/">About Us</Link></li>
<li className="RightNav"><Link to="/">Contact Us</Link></li>
<div className="btnflexright">
<div
className={"controller " + (this.state.isLoginOpen
? "selected-controller"
: "")}
onClick={this
.showLoginBox
.bind(this)}>
Login
</div>
<div
className={"controller " + (this.state.isRegisterOpen
? "selected-controller"
: "")}
onClick={this
.showRegisterBox
.bind(this)}>
Sign up
</div>
</div>
</ul>
</div>
</Router>
);
}
}
export default Navigation;
This is the main screen where the login and Sign form will go:
import React from 'react';
import RegisterBox from '../Forms/Register'
import LoginBox from '../Forms/Register'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoginOpen: true,
isRegisterOpen: false
};
}
showLoginBox() {
this.setState({isLoginOpen: true, isRegisterOpen: false});
}
showRegisterBox() {
this.setState({isRegisterOpen: true, isLoginOpen: false});
}
render() {
return (
<div>
<div className="root-container">
{this.state.isLoginOpen && <LoginBox/>}
{this.state.isRegisterOpen && <RegisterBox/>}
</div>
</div>
)
}
}
export default App;
Login and Sign up Form are the same but in two different files Login.js and Register,js I will show the code for that one now.
//Login Box
import React from 'react';
class LoginBox extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<div className="formContent modal-main">
<h2>Welcome Back <span>Brandon!</span></h2>
<form>
<input
type="text"
name="email"
placeholder="Email Address"
/>
<input
name="password"
type="text"
placeholder="Password"
/>
<div className="passContent">
<div className="checkingPass">
<input
className="inline"
type="checkbox"
name="check"
value="Remember Password"
/>
<span
className="inline">
Remember Password
</span>
</div>
<p
className="passFont">
Forgot Password
</p>
</div>
<input
className="formmbtn"
type="button"
name="button"
alue="Login"
/>
<div
className="social-media-button">
<input
className="clearbtn"
type="button"
name="button"
value="Sign in with Facebook"
/>
<div
className="divider"
/>
<input
className="clearbtn"
type="button"
name="button"
value="Sign in with Facebook"
/>
</div>
<p
className="passFont">
Don't have an account?
<span>Sign up</span>
</p>
</form>
</div>
</div>
)
}
}
export default LoginBox;
The class Navigation doesn't have the functions showRegisterBox and showLoginBox, so this.showRegisterBox is undefined (that's why you get the Cannot read property 'bind' of undefined error when you do this.showRegisterBox.bind() on the Navigation Component.
Possible solutions:
If those functions aren't defined in the Navigation component, then you should probably pass them as props when you instantiate Navigation. That's the usual way a component communicates with it's parent component.
So if for example you were to use your Navigation component in App (which is where those functions are defined), you would do:
<Navigation showRegisterBox={this.showRegisterBox.bind(this)} showLoginBox={this.showLoginBox.bind(this)}/>
Then in Navigation you use them like such:
onClick={this.props.showRegisterBox}

React-Redux Application: 'props' is not defined'

I am creating a React-Redux application. Before adding the content above render(), props was defined. However, after I started adding said content, the app began having problems reading the variable. I don't know what happened.
Could somebody please help me understand why props isn't being recognized anymore? I'd really appreciate it.
Login.js
import React from 'react';
import Modal from 'react-bootstrap/Modal';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import './login.css';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: ''
}
this.onChange = this.onChange.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value});
}
render() {
return (
<>
<Modal show={props.modalOpen} onHide={props.handleModalOpen}>
<Modal.Header closeButton>
<Modal.Title>Login</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form>
<Form.Group>
<Form.Label>Username</Form.Label>
<Form.Control
type="text"
name="username"
className="form-control"
/>
</Form.Group>
<Form.Group>
<Form.Label>Password</Form.Label>
<Form.Control
type="text"
name="password"
className="form-control"
/>
</Form.Group>
</Form>
</Modal.Body>
<Modal.Footer>
<Button variant="danger" type="submit" onClick={props.handleModalOpen}>
Submit
</Button>
<Button variant="primary" onClick={props.handleClose}>
Cancel
</Button>
</Modal.Footer>
</Modal>
</>
);
}
}
export default LoginForm;
Navbar.js
import React from "react";
import { Link } from 'react-router-dom';
import './navbar.css';
import LoginForm from '../LoginForm/login.js';
// import SignUpModal from '../SignUpModal/signup.js';
class Navbar extends React.Component {
state = {
modalOpen: false
}
handleModalOpen = () => {
this.setState((prevState) => {
return {
modalOpen: !prevState.modalOpen
}
})
}
render() {
return (
<div>
<nav className="navbar navbar-expand-sm navbar-dark px-sm-5">
<div className="container">
<Link to='/'>
<div className="navbar-brand">
<i class="fas fa-globe fa-2x"></i>
</div>
</Link>
<ul className="navbar-nav align-items-right">
<li className="nav-item ml-5">
<a onClick={this.handleModalOpen} className="nav-link">
Login
</a>
</li>
<li className="nav-item ml-5">
<a onClick={this.handleModalOpen} className="nav-link">
Sign Up
</a>
</li>
</ul>
</div>
</nav>
<LoginForm
modalOpen={this.state.modalOpen}
handleModalOpen={this.handleModalOpen}
/>
{/* <SignUpModal
modalOpen={this.state.modalOpen}
handleModalOpen={this.handleModalOpen}
/> */}
</div>
)
}
}
export default Navbar;
In your Login.js, it's written using class-based syntax, hence it has to be this.props instead of props
<Modal show={this.props.modalOpen} onHide={this.props.handleModalOpen}>
Alternatively, Login.js can be rewrite into functional-based component, you can then use props like below
const Login = props => {
return (
<Modal show={props.modalOpen} onHide={props.handleModalOpen} />
)
}

store user data globally in react redux for login logout purpose

i am new to react and redux, i just have an idea of both using that i am creating an app where i need to store the userData globally so that it is accessible via the whole application. i return userData the from backend using lumen but how to pass it to whole application components like header and other components to manage authentication.
below i paste the code, could any one help me to finish this.
Login component alone i have worked, not in redux as i am not good at it
import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import "./assets/vendor/fontawesome-free/css/all.min.css";
import "./assets/css/sb-admin-2.min.css";
import "bootstrap/dist/js/bootstrap.bundle.min";
import validator from "simple-react-validator";
import confData from "./../Config/Config";
const apiKey = confData.apiKey;
const apiURL = confData.apiURL;
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false,
email: "",
password: "",
uData: [],
id: ""
};
this.validator = new validator(this);
}
componentDidMount = () => {
document.title = "Sample | Login";
};
loginSubmit(e) {
e.preventDefault();
if (this.validator.allValid()) {
let email = this.state.email;
let password = this.state.password;
var self = this;
axios
.post(apiURL + "auth/login", {
email: email,
password: password
})
.then(function(response) {
console.log(response.data);
self.isLoggedIn = true;
self.uData = response.data;
self.setState({
email: self.uData.email,
id: self.uData.id,
isLoggedIn: true
});
localStorage.setItem("token", JSON.stringify(self.uData));
})
.catch(function(error) {
console.log(error.response);
this.isLoggedIn = false;
});
} else {
this.validator.showMessages();
// rerender to show messages for the first time
this.forceUpdate();
}
}
handleEmailChange(event) {
this.setState({
email: event.target.value
});
}
handlePasswordChange(event) {
this.setState({
password: event.target.value
});
}
render() {
return (
<div className="row justify-content-center">
<div className="col-xl-10 col-lg-12 col-md-9">
<div className="card o-hidden border-0 shadow-lg my-5">
<div className="card-body p-0">
<div className="row">
<div className="col-lg-6 d-none d-lg-block"></div>
<div className="col-lg-6">
<div className="p-5">
<div className="text-center">
<h1 className="h4 text-gray-900 mb-4">Welcome Back!</h1>
<Notifications options={{ zIndex: 200, top: "50px" }} />
</div>
<form
className="user"
onSubmit={this.loginSubmit.bind(this)}
>
<div className="form-group">
<input
type="email"
className="form-control form-control-user"
id="exampleInputEmail"
aria-describedby="emailHelp"
placeholder="Enter Email Address..."
onChange={this.handleEmailChange.bind(this)}
value={this.state.email}
/>
<span style={{ color: "#ff0000" }}>
{this.validator.message(
"Email",
this.state.email,
"required|email"
)}
</span>
</div>
<div className="form-group">
<input
type="password"
className="form-control form-control-user"
id="exampleInputPassword"
placeholder="Password"
onChange={this.handlePasswordChange.bind(this)}
value={this.state.password}
/>
<span style={{ color: "#ff0000" }}>
{this.validator.message(
"Password",
this.state.password,
"required"
)}
</span>
</div>
<button className="btn btn-primary btn-user btn-block">
Login
</button>
<hr />
<Link
to="#"
className="btn btn-google btn-user btn-block"
>
<i className="fab fa-google fa-fw"></i> Login with
Google
</Link>
<Link
to="#"
className="btn btn-facebook btn-user btn-block"
>
<i className="fab fa-facebook-f fa-fw"></i> Login with
Facebook
</Link>
</form>
<hr />
<div className="text-center">
<Link className="small" to="#">
Forgot Password?
</Link>
</div>
<div className="text-center">
<Link className="small" to="#">
Create an Account!
</Link>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
here i get the json return data from lumen, now i need to authenticate and maintain data all over the components using redux and autheenter code here and authenticate pages after user login.
Checkout redux docs first, and they have good example for newbies https://redux.js.org/basics/example
And user prettier, 2 space tabs (instead of 8), object destructurein https://dev.to/sarah_chima/object-destructuring-in-es6-3fm your code is pretty unreadable and NEVER use jquery with react.

How to change the state of an item affected by onClick in array?

I'm making a page where I need to make multiple selections of buttons (like a filter, which I'll use for the next page).
the information from these buttons is coming from an array and I'm using .map () to mount the button list.
My problem is how do I change the state of only the button that was clicked. The way it is now, when I click, all the buttons are active.
How can I solve this?
Thank you.
import React from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import messages from './messages';
import { getLevel, getDiscipline } from '../../functions';
import template from './index.pug';
export default class ConfigAssessment extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function
constructor(props){
super(props);
this.state = {
level: getLevel(),
discipline: getDiscipline(),
active: '',
first_click: true,
}
}
changeActive = () => {
if (this.state.first_click === true) {
this.setState({
active: 'active',
first_click: false
});
} else {
this.setState({
active: '',
first_click: true,
});
}
}
render() {
return(
<div className="configuration">
<div className="config-title">
<i className="ti-settings" />
<h2>
<FormattedMessage {...messages.configAssessment} />
</h2>
</div>
<div className="config-items">
<div className="form-group">
<label>
<FormattedMessage {...messages.level} />
</label>
<div className="row">
{this.state.level.map((level, i) => (
<div className="col-xs-1 col-md-4 col-lg-3" key={level.id}>
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
id={level.id}
onClick={this.changeActive}
>
{level.level}
</button>
</div>
))}
</div>
</div>
<div className="form-group">
<label>
<FormattedMessage {...messages.discipline} />
</label>
<div className="row">
{ this.state.discipline.map((discipline, i) => (
<div className="col-xs-1 col-md-4 col-lg-3" key={i}>
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
onClick={this.changeActive}
>
{discipline.discipline}
</button>
</div>
))}
</div>
</div>
<div className="form-group">
<label>
<FormattedMessage {...messages.selectQuestion} />
</label>
<div className="row">
<div className="col-xs-1 col-md-4 col-lg-3">
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
onClick={this.changeActive}
>
<FormattedMessage {...messages.typeAutomatic} />
</button>
</div>
<div className="col-xs-1 col-md-4 col-lg-3">
<button
className={`btn btn-light-gray btn-block ${this.state.active}`}
onClick={this.changeActive}
>
<FormattedMessage {...messages.typeManual} />
</button>
</div>
</div>
</div>
<div className="form-group fg-right">
<Link className="btn btn-warning" to="#">
<FormattedMessage {...messages.createAssessment} />
</Link>
</div>
</div>
</div>
);
}
}
Create a separate component for button
class MyButton extends Component {
constructor(props){
super(props);
this.state = {
person: this.props.person
}
}
buttonActiveHandler = () => {
let oldStatus = this.props.person.status;
this.props.person.status = (!oldStatus ? 'active': '');
this.setState({
person:this.props.person
});
}
render() {
return (
<button className={this.state.person.status} onClick={this.buttonActiveHandler}>{this.state.person.name}</button>
);
}
}
export default MyButton;
Then import button component. use map function to for your code block
<div className={classes.Box}>
<h4>Lorem, ipsum.</h4>
{
this.props.survey.map((person, i) => {
return (
<MyButton key={i} person={person}/>
)
})
}
</div>
The easiest solution to this problem is making the component of the content inside the map and then handling the state of that component there. Hence it will maintain individual states.
It depends what you need.
You can create separate component for button with state.
You can also keep state of each button in react state as an array, and then you can get the state of each button by index.
I'd recommend the first solution, it'd easier to manage such state, but it will be harder to get the state of a specific button from the parent component.

How to render two components so that both content shows on one page

I have a login and signup form using modals. They both work just fine, but when rendering them in my home page only the login form will pop up for both but if I switch the order signup will show up for both. I'm not sure if I am missing something or need to wrap them up differently.
This is my signup but my login is pretty much the same just with different naming.
import React from "react";
import SignUp from "../SignUp";
import Login from "../Login";
const Home = () => (
<div>
<Login />
<SignUp />
</div>
);
export default Home;
import React, { Component } from "react";
import axios from "axios";
import { Redirect } from "react-router-dom";
class SignUp extends Component {
constructor() {
super()
this.state = {
username: '',
password: '',
confirmPassword: '',
redirectTo: null
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
};
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
};
handleSubmit(event) {
event.preventDefault()
// TODO - validate!
axios
.post('/auth/signup', {
username: this.state.username,
password: this.state.password
}).then(response => {
console.log(response)
if (!response.data.errmsg) {
console.log('youre good')
this.setState({
redirectTo: '/login'
});
} else {
console.log('duplicate')
}
});
};
render() {
if (this.state.redirectTo) {
return <Redirect to={{ pathname: this.state.redirectTo }} />
}
return (
<div>
<a data-toggle="modal" data-
target="#exampleModal">Signup</a>
<div className="modal fade" id="exampleModal" tabIndex="-1"
role="dialog" aria-labelledby="exampleModalLabel" aria-
hidden="true">
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title"
id="exampleModalLabel">Sign Up</h5>
<button type="button" className="close" data-
dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div className="container ">
<div className="modal-body">
<div className="SignupForm">
<label
htmlFor="username">Username</label><br />
<input type="text" name="username"
value={this.state.username} onChange=
{this.handleChange} /><br />
<label
htmlFor="password">Password</label><br />
<input type="password"
name="password" value={this.state.password}
onChange=
{this.handleChange} /><br />
<label htmlFor="confirmPassword">Confirm
Password</label>
<br />
<input type="password"
name="confirmPassword" value=
{this.state.confirmPassword} onChange=
{this.handleChange}/>
</div>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary"
data-dismiss="modal">Close</button>
<button type="submit" onClick={this.handleSubmit}
className="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
};
export default SignUp;
Usage of Bootstrap JS Modals will interfere with the proper functioning React's Virtual DOM.
Kindly use a react-compatible modal, such as: Material-Ui Dialog
Your component looks alright. Taking by your word, if they render perfectly on separate pages. Few points i'll want you to look for
If you are directly rendering them as two directly by attaching both components straightaway
render() {<div> <SignUp /> <Login /> </div> }
If this causes you to open the same component twice, you might want to check the id's of these elements. It's quite probable that both of your components are defined by the same id and the one which renders first, gets called first.
Also, if you want to place some conditions on these components, you can assign them in states, and set them according to the conditions.

Resources