Home > Enterprise >  Unable to render React page after refresh, instead showing raw data
Unable to render React page after refresh, instead showing raw data

Time:03-22

everyone! I am trying to build an app using Node/React/Mongoose without CRA command that also includes custom webpack.

I managed to build everything in a single React page (App.jsx), but the code looks messy. I managed to build it by fetching the data from database first, and later building everything around it. Now that works, but I wanted to go one step further. I wanted to build an app regardless of the data being fetched.

Now, the problem I found myself having is maybe better to show in a slideshow. How do I solve this issue? I've tried changing the paths, but no luck.

Once I am on a homepage, navbar is being rendered. I can also refresh, and it won't break. enter image description here

When I use navbar to navigate to a /teacher, it loads everything nicely.

enter image description here

But the problem starts when I refresh OR type the url localhost8080/teacher manually. Instead of a component, it fetches the raw data.

enter image description here

This is my code relevant to the issue:

server.js

app.use('/teacher', authRoutes);
app.use('/class', classRoutes);
app.get('/', (req, res, next) => {
  
  res.sendFile(path.resolve(__dirname, "../docs/index.html"))

})

routes.js

router.get('/signup', authController.teacherList);

controller

exports.teacherList = (req, res, next) => {
    Teacher.find()
    .then(teacher => {
        return res.send(teacher)
    })
    .catch(err => console.log(err));
}

App.jsx

import React, { Component } from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import TeacherComponent from './Teacher'
import ClassComponent from './Class'

export default class App extends Component {
    render() {    
      return (
    <Router>
        <div>
          <nav className="navbar navbar-expand-lg navbar-light bg-light">
          <ul className="navbar-nav mr-auto">
          <li><Link to={'/class'} className="nav-link">Class</Link></li>
            <li><Link to={'/teacher'} className="nav-link">Teacher</Link></li>
          </ul>
          </nav>
          <hr />
          <Routes>
              <Route path='/class' element={<ClassComponent />} />
              <Route path='/teacher' element={<TeacherComponent />} />
          </Routes>
        </div>
      </Router>
      );
    }
  }

Teacher.jsx

import React, { Component } from 'react';
import { Redirect } from 'react-router-dom'
import Select from 'react-select';
import 'bootstrap/dist/css/bootstrap.min.css';

class Teacher extends Component {
  
    constructor(props) {
        super(props);
        this.state = {
          teachers: [],
          isSignedUp: false,
          firstName : '',
          lastName: '',
          email: '',
          password: '',
          studentsClass: {
              students: []
          }
        };
      }

      
    componentDidMount() {
        
        fetch('http://localhost:8080/teacher')
          .then(response => response.json())
          .then(teachers => this.setState({teachers: teachers}));
    }

    handleFirstName = (e) => {
        this.setState({firstName: e.target.value});
    }
    handleLastName = (e) => {
        this.setState({lastName: e.target.value});
    }
    handleEmail = (e) => {
        this.setState({email: e.target.value});
    }
    handlePassword = (e) => {
        this.setState({password: e.target.value});
    }

    signUp = () => {
        const requestOptions = {
            method: 'POST',
            headers: {
              Accept: 'application/json',
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({firstName: this.state.firstName , lastName: this.state.lastName, email: this.state.email , password: this.state.password}),
          };
          fetch("/teacher/signup", requestOptions)
          .then((response) => {
            return response.json();
          })
          .then(() => {
                fetch('http://localhost:8080/class')
                .then(response => response.json())
                .then(teachers => this.setState({teachers: teachers}));
          })
          .catch((err) => {
              console.log(err)
          })
    }

    render() {
        // const teachers = this.state.teachers.map(teacher => <div key={teacher._id}>{teacher.firstName} - {teacher.lastName}</div>);
        const teachers = []
        for (let i = 0; i < this.state.teachers.length; i  ) {
            teachers.push({label: this.state.teachers[i].firstName   ' '   this.state.teachers[i].lastName})
        }
        return (
            <div>
            <div className="container">
            <div className="row" style={{marginTop: "100px"}}>
              <div className="col-md-4"></div>
              <div className="col-md-4">
                <Select placeholder="Select teacher"options={ teachers } />
              </div>
              <div className="col-md-4"></div>
              
            </div>
            
          </div>
                        <div></div>
                        <div style={{display: "flex", alignItems: "center", justifyContent: "center", marginTop: "100px"}}><h5>Signup:</h5></div>
                        <div style={{margin: "20px auto", borderTop: "2px solid black", width: "400px", textAlign: "center"}}>
                        <form style={{marginTop: "30px"}} onSubmit={(event) => {
                            event.preventDefault()
                            this.signUp(this.state.teacher)
                        }} >
                                <input style={{textAlign: "center"}} type="text" name="firstName" placeholder="First Name" value={this.state.firstName} onChange={this.handleFirstName} />
                                <div></div>
                                <br></br>
                                <input style={{textAlign: "center"}} type="text" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleLastName}/>
                                <div></div>
                                <br></br>
                                <input style={{textAlign: "center"}} type="text" name="email" placeholder="Email" value={this.state.email} onChange={this.handleEmail}/>
                                <div></div>
                                <br></br>
                                <input style={{textAlign: "center"}} type="password" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePassword}/>
                                <div></div>
                                <br></br>
                                <input className="button button2" type="submit" />
                        </form>
                        </div>
                        </div>
        );
      }
}

export default Teacher;

And the webpack, that is proxying the requests:

const webpack = require("webpack");
const path = require("path");

module.exports = {
  entry: path.resolve(__dirname, "./src/index.js"),
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: ["babel-loader"],
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"]
      },
      {
        test: /\.(jpe?g|png|gif|svg)$/i, 
        use: ["file-loader"]
      }
    ],
  },
  resolve: {
    extensions: ["*", ".js", ".jsx"],
  },
  mode: process.env.NODE_ENV,
  output: {
    path: path.resolve(__dirname, "./docs"),
    filename: "bundle.js",
  },
  // [webpack-dev-server] "hot: true" automatically applies HMR plugin, you don't have to add it manually to your webpack configuration.
  plugins: [new webpack.HotModuleReplacementPlugin()],
  devServer: {
    
    // contentBase is deprecated by static in webpack v5
    proxy: {
      '/': 'http://localhost:3000/',
    },
    // contentBase: path.resolve(__dirname, "./docs"),
    hot: true,
  },
};

CodePudding user response:

The issue is how you have configured your server.js file

app.use('/teacher', authRoutes);
app.use('/class', classRoutes);
app.get('/', (req, res, next) => {
  
  res.sendFile(path.resolve(__dirname, "../docs/index.html"))

})

Now imagine you are sending request to your server to fetch the data in url /teacher now it is doing it's job perfectly fine. Because, it would come across the first line and send you the raw json and be done with it.

One solution would be to keep all your api modules in an '/api' appended path. So, they don't conflict with your regular routing.

app.use('/api/teacher', authRoutes);
app.use('/api/class', classRoutes);
app.get('/', (req, res, next) => {
  
  res.sendFile(path.resolve(__dirname, "../docs/index.html"))

})

This should solve your issue.

EDIT: Last route should always return home page. So, a star is needed in path-matching

app.get('/*', (req, res, next) => {
  
  res.sendFile(path.resolve(__dirname, "../docs/index.html"))

})

CodePudding user response:

your sever is running on localhost 8080, so might be client is running on localhost something else like 3030 please check that because as i am seeing that you're calling server api on 8080.

you can optimise your code this -

          <Route path='/class' element={()=><ClassComponent teacher={this.state.teachers} />} />
          <Route path='/teacher' element={()=><TeacherComponent teacher={this.state.teachers/>} />

try this & please upload the ClassComponent and TeacherComponent as well.

  • Related