1

i want to display every error messages from Django Rest Api automatically in React frontend. i wanted to test my concept with the signup authentication function and after fixing it i would like to use the concept in every components in fetching data from or into django API.

here is my App.js to register a user just for test:

import React, { useState } from "react";

export default function Signup() {
  const [username, setUsername] = useState("");
  const [email, setEmail] = useState("");
  const [password1, setPassword1] = useState("");
  const [password2, setPassword2] = useState("");
  
  const [user, setUser] = useState("");
  function handleEmail(evt) {
    setEmail(evt.target.value);
  }
  function handleUsername(evt) {
    setUsername(evt.target.value);
  }

  function handlePassword1(evt) {
    setPassword1(evt.target.value);
  }
  function handlePassword2(evt) {
    setPassword2(evt.target.value);
  }
  function handle_signup(evt) {
    evt.preventDefault();
    fetch("http://127.0.0.1:8000/api/v1/rest-auth/registration/", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ username, email, password1, password2 }),
    })
      .then((res) => res.json())
      .then((json) => {
        localStorage.setItem("token", json.key);
        console.log(json);
        setUser(json.username);
      })
      .catch((err) => {
        if(err.res){
          console.log(err.res.username)
          console.log(err.res.email);
          console.log(err.res.password1);
          console.log(err.res.password2);
          
        }else if(err.res){
          console.log(err.res)
          
        }else{
          console.log('Error',err.message)
        }
        console.log(err.config);
      });
  }

  return (
    <form onSubmit={(evt) => handle_signup(evt, setUser())}>
      <label htmlFor="register-username">Username:</label>
      <input
        type="text"
        value={username}
        onChange={handleUsername}
        name="register-username"
        id="register-username"
      />
      <label htmlFor="register-email">Email:</label>
      <input
        type="text"
        value={email}
        onChange={handleEmail}
        name="register-username"
        id="register-username"
      />
      <label htmlFor="register-password1">Password1:</label>
      <input
        type="password1"
        value={password1}
        onChange={handlePassword1}
        name="register-password1"
        id="register-password1"
      />
      <label htmlFor="register-password2">password2:</label>
      <input
        type="password2"
        value={password2}
        onChange={handlePassword2}
        name="register-password2"
        id="register-password2"
      />
      <input type="submit" value="Register" />
    </form>
  );
}

in UseEffect i have tried to show every error message under relevant input boxes which are username, email, password1, password2, i tried to do it by React-hook-form but it will be like inserting error messages from frontend. but i want to show actual error messages from backend. in development tools, when i try upper codes by putting wrong infos in input boxes, it would only show POST: 400 (bad request)

how can i show such error messages under every input boxes like Username exist or email address is invalid, or password must be at least 8 which are typical in Django Rest API's typical error messages ?

FYI: this code can register any user if the input boxes are correctly filled up.

1 Answer 1

2

The code below is from my article React Token-Based Authentication to Django REST API Backend. It is using react-bootstrap. It is a simple example with username and password, but you can easily extend it.

If there is a known error (axios docs about handling errors) I check if it has a message for username or password. If yes, then I set an error message for FormControl.Feedback. If you don't want to use react-bootstrap you can just make a small red text inside div and make it visible only if the error message is set (not empty).

// frontend/src/components/SignupReducer.js

// import needed actions
import {
  CREATE_USER_ERROR,
  CREATE_USER_SUBMITTED,
  CREATE_USER_SUCCESS
} from "./SignupTypes";

// define the initial state of the signup store
const initialState = {
  usernameError: "",
  passwordError: "",
  isSubimtted: false
};

// define how action will change the state of the store
export const signupReducer = (state = initialState, action) => {
  switch (action.type) {
    case CREATE_USER_SUBMITTED:
      return {
        usernameError: "",
        passwordError: "",
        isSubimtted: true
      };
    case CREATE_USER_ERROR:
      const errorState = {
        usernameError: "",
        passwordError: "",
        isSubimtted: false
      };
      if (action.errorData.hasOwnProperty("username")) {
        errorState.usernameError = action.errorData["username"];
      }
      if (action.errorData.hasOwnProperty("password")) {
        errorState.passwordError = action.errorData["password"];
      }
      return errorState;
    case CREATE_USER_SUCCESS:
      return {
        usernameError: "",
        passwordError: "",
        isSubimtted: false
      };
    default:
      return state;
  }
}
// frontend/src/components/signup/SignupActions.js

import axios from "axios";
import { toast } from "react-toastify";
import { isEmpty } from "../../utils/Utils";
import {
  CREATE_USER_ERROR,
  CREATE_USER_SUBMITTED,
  CREATE_USER_SUCCESS
} from "./SignupTypes";

export const signupNewUser = userData => dispatch => {
  dispatch({ type: CREATE_USER_SUBMITTED }); // set submitted state
  axios
    .post("/api/v1/users/", userData)
    .then(response => {
      toast.success(
        "Account for " +
          userData.username +
          " created successfully. Please login."
      );
      dispatch({ type: CREATE_USER_SUCCESS });
    })
    .catch(error => {
      if (error.resposne) {
        // The request was made and the server responded with a status code
        // that falls out of the range of 2xx
        toast.error(JSON.stringify(error.response.data));
        dispatch({
          type: CREATE_USER_ERROR,
          errorData: error.response.data
        });
      } else if (error.message) {
        // the error message is available,
        // let's display it on error toast
        toast.error(JSON.stringify(error.message));
      } else {
        // strange error, just show it
        toast.error(JSON.stringify(error));
      }
    });
};
// frontend/src/components/signup/Signup.js file

import React, { Component } from "react";
import { withRouter } from "react-router-dom"; // new import
import { connect } from "react-redux"; // new import
import PropTypes from "prop-types"; // new import
import { Link } from "react-router-dom";
import {
  Container,
  Button,
  Row,
  Col,
  Form,
  FormControl
} from "react-bootstrap";

import { signupNewUser } from "./SignupActions"; // new import

class Signup extends Component {
  constructor(props) {
    super(props);
    this.state = {
      username: "",
      password: ""
    };
  }
  onChange = e => {
    this.setState({ [e.target.name]: e.target.value });
  };

  // update function to call the action
  onSignupClick = () => {
    const userData = {
      username: this.state.username,
      password: this.state.password
    };
    this.props.signupNewUser(userData); // <-- signup new user request
  };

  render() {
    return (
      <Container>
        <Row>
          <Col md="4">
            <h1>Sign up</h1>
            <Form>
              <Form.Group controlId="usernameId">
                <Form.Label>User name</Form.Label>
                <Form.Control
                  isInvalid={this.props.createUser.usernameError}
                  type="text"
                  name="username"
                  placeholder="Enter user name"
                  value={this.state.username}
                  onChange={this.onChange}
                />
                <FormControl.Feedback type="invalid">
                  {this.props.createUser.usernameError}
                </FormControl.Feedback>
              </Form.Group>

              <Form.Group controlId="passwordId">
                <Form.Label>Your password</Form.Label>
                <Form.Control
                  isInvalid={this.props.createUser.passwordError}
                  type="password"
                  name="password"
                  placeholder="Enter password"
                  value={this.password}
                  onChange={this.onChange}
                />
                <Form.Control.Feedback type="invalid">
                  {this.props.createUser.passwordError}
                </Form.Control.Feedback>
              </Form.Group>
            </Form>
            <Button color="primary" onClick={this.onSignupClick}>
              Sign up
            </Button>
            <p className="mt-2">
              Already have account? <Link to="/login">Login</Link>
            </p>
          </Col>
        </Row>
      </Container>
    );
  }
}

// connect action and reducer
// replace 
// export default Signup;
// with code below:

Signup.propTypes = {
  signupNewUser: PropTypes.func.isRequired,
  createUser: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  createUser: state.createUser
});

export default connect(mapStateToProps, {
  signupNewUser
})(withRouter(Signup));

signup with errors

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.