javascript - Mallen laddas inte upp i sidofältet utan uppdatering (React)


Jag är ny på React och jag har försökt att ladda upp några mallar till backend-servern så att de visas i sidofältet, men det fungerar inte förrän sidan uppdateras (min gissning är att sidan behöver renderas igen). Jag har implementerat en fönster-uppdatering vid klick på uppladdningsknappen, men jag undrar om det finns en bättre lösning för detta. Mina koder för mallarna finns nedan. All hjälp skulle uppskattas mycket eftersom jag är ny.

Backend App.js

const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const fileUpload = require('express-fileupload');
const cors = require('cors');

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(cors());
app.use(fileUpload());
app.use('/public', express.static(__dirname + '/public'));


app.post('/upload', (req, res, next) => {
  console.log(req);
  let xmlFile = req.files.file;

  xmlFile.mv(`${__dirname}/public/${req.body.filename}.xml`, function(err) {
    if (err) {
      return res.status(500).send(err);
    }

    res.json({file: `public/${req.body.filename}.xml`});
  });

})

app.get('/file', function (req, res) {
  const fs = require('fs');
  //joining path of directory 
  const directoryPath = path.join(__dirname, 'public');
  //passsing directoryPath and callback function
  fs.readdir(directoryPath, function (err, files) {
    //handling error
    if (err) {
      return console.log('Unable to scan directory: ' + err);
    } else {
      res.json({files: files});
    }
    //listing all files using forEach
    files.forEach(function (file) {
      // Do whatever you want to do with the file
      console.log(file);
    });
  });
});

app.get('/file/:fileName', function (req, res) {
  const fs = require('fs');
  console.log(req.params);
  const directoryPath = path.join(__dirname, 'public');
  fs.readFile(directoryPath + '\\'+ req.params.fileName, 'utf8', function (err, data) {
    //handling error
    if (err) {
      return console.log('Unable to read file: ' + err);
    } else {
      res.json({content: data});
    }
  });
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
  const err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

app.listen(8000, () => {
  console.log('8000');
});

module.exports = app;
`

App.tsx

import * as React from 'react';
import './sass/App.css';
import 'primereact/resources/primereact.min.css';
import 'primereact/resources/themes/omega/theme.css';
import 'font-awesome/css/font-awesome.css';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
import './resources/primereact.css';
import * as classNames from 'classnames';
import { BrowserRouter as Router, Redirect, Route, Switch, Link } from 'react-router-dom';
import Sidebar from './Sidebar';
import Home from './Home';
import CapitalHomelessness from './CapitalHomelessness';
import FormTemplateComponent from './FormTemplateComponent';
import { FileUploadComponent } from './FileUploadComponent';
import { Menu } from 'primereact/components/menu/Menu';

interface AppProps { }
interface AppState { 
    loadedContent: Document; 
    loadedCapitalHomelessnessContent: Document;
    menuActive: boolean;
    langCo: string;
    items: any;
}

class App extends React.Component<AppProps, AppState> {
    loadedContent: Document;
    formItems = [];
    menu: Menu;

    constructor(props: AppProps) {
        super(props);
        this.state = {
            loadedContent: null,
            loadedCapitalHomelessnessContent: null,
            menuActive: false,
            langCo: 'en',
            items: [
                {
                    label: 'Admin',
                    items: [
                        {label: 'Home', icon: 'fa fa-home', 
                            command: () => { window.location.href = '/Home'; }},
                        {label: 'New Template', icon: 'fa fa-plus-square', 
                            command: () => { window.location.href = '/FileUploadComponent'; }},
                            {label: 'List Template', icon: 'fa fa-list-ol', 
                            command: () => { window.location.href = '/CapitalHomelessness'; }}
                    ]
                }, 
                {
                    label: 'Form',
                    items: this.formItems
                }
            ],
        };

        this.openMenu = this.openMenu.bind(this);
        this.closeMenu = this.closeMenu.bind(this);
        this.onSidebarClick = this.onSidebarClick.bind(this);
        this.getMenuItems = this.getMenuItems.bind(this);
        this.switchLang = this.switchLang.bind(this);
    }

    async componentWillMount() {
       await this.getMenuItems();
       this.setState({ menuActive: !this.state.menuActive });
    }
    componentDidMount() {
        // this.formItems = [];
        // this.getMenuItems();
        // tslint:disable-next-line:no-console
        console.log(this.state.items);
    }
    // tslint:disable-next-line:typedef
    openMenu(event) {
        // tslint:disable-next-line:no-console
        console.log(event);
        this.setState({ menuActive: !this.state.menuActive });
        event.preventDefault();
    }

    // tslint:disable-next-line:typedef
    closeMenu(event) {
        this.setState({ menuActive: false});
        event.preventDefault();
    }

    // tslint:disable-next-line:typedef
    onSidebarClick(event) {
        // tslint:disable-next-line:no-console
        console.log(event);
        if (event.target.nodeName === 'A' && event.target.parentNode.className.indexOf('layout-menu') === -1) {
            this.closeMenu(event);
        }
    }

    async getMenuItems() {
        const response = await fetch('http://localhost:8000/file');
        const json = await response.json();
        json.files.forEach(async element => {
            this.formItems.push({
                label: element.split('.').slice(0, -1).join('.'), icon: 'fa fa-file',
                command: async () => {
                    window.location.href = `/FormLoading/:${element}`;
                    this.forceUpdate();
                }
            });
        });
    }

    switchLang() {
        const newLancCode = (this.state.langCo === 'fr' ? 'en' : 'fr');
        this.setState({ langCo: newLancCode });
    }

    render() {
        return (
            <Router>
                <div className="layout-wrapper">
                    <div id="layout-topbar">
                        <a className="menu-button" onClick={this.openMenu}>
                            <i className="fa fa-bars" />
                        </a>
                        <Link to="/" className="logo">
                            <img alt="logo" src="resources/images/ontario-logo.png" id="top-logo" height="50px" width="100px"/>
                        </Link>
                        <a className="title">
                            <h3>Dynamic Template Form</h3>
                        </a>
                        <ul className="topbar-menu">
                            <li>
                                <Link to="/signin"> Sign in </Link>
                            </li>
                            <li>
                                <Link to="#" onClick={this.switchLang}> {this.state.langCo === 'fr' ? 'english' : 'Français'} </Link>
                            </li>
                        </ul>
                    </div>
                    {/* tslint:disable-next-line:max-line-length */}
                    <div id="layout-sidebar" className={classNames({ 'active': this.state.menuActive === true })} onClick={this.onSidebarClick}>
                        <div className="nano-content">
                            <div>
                                <Menu model={this.state.items} ref={el => this.menu = el}/>
                            </div>
                        </div>
                    </div>
                    <div id="layout-content">
                        <Switch>
                            <Route path="/" exact={true} component={Home} />
                            <Route
                                path="/Home"
                                render={(props) => <Home {...props} />}
                            />
                            <Route
                                path="/FileUploadComponent"
                                render={(props) => <FileUploadComponent {...props} />}
                            />
                            <Route
                                path={`/FormLoading/:fileName`}
                                render={(props) => <FormTemplateComponent {...props} langCo={this.state.langCo}/>}
                            />
                        </Switch>
                    </div>
                </div>
            </Router>
        );
    }
}

export default App;
`

Filuppladdningskomponent.tsx

import * as React from 'react';
import { Growl, GrowlMessage } from 'primereact/components/growl/Growl';
import { Panel } from 'primereact/components/panel/Panel';
import { Button } from 'primereact/components/button/Button';
import sidebar from './Sidebar';

interface Props {
}

interface State {
    imageURL: string;
}

export class FileUploadComponent extends React.Component<Props, State> {
    growl: Growl;
    fileName: HTMLInputElement;
    uploadInput: HTMLInputElement;

    constructor(props: Props) {
        super(props);
        this.state = {
            imageURL: '',

        };

        this.setState(this.state);
        this.handleUploadImage = this.handleUploadImage.bind(this);
    }

    handleUploadImage(ev: any) {
        ev.preventDefault();

        const data = new FormData();
        data.append('file', this.uploadInput.files[0]);
        data.append('filename', this.fileName.value);

        fetch('http://localhost:8000/upload', {
            method: 'POST',
            body: data,
        }).then((response) => {
            response.json().then((body) => {
                this.setState({ imageURL: `http://localhost:8000/${body.file}` });
            });
        });      
        // this.growl.props.show({severity: 'info', summary: 'Success', detail: 'File Uploaded', closable: true, sticky: false, life: 1});
        // tell sidebar component to update its menu items

    }
    reloadPage () {
        window.location.reload();
      }

    render() {   
        return (
            <Panel header="Template Uploading">
            <form onSubmit={this.handleUploadImage}>
                <div className="ui-g-12">
                    <input ref={(ref) => { this.uploadInput = ref; }} type="file" />
                </div>
                <div className="ui-g-12">
                    <input ref={(ref) => { this.fileName = ref; }} type="text" placeholder="Enter the name of file" /> *Required
                </div>
                <br />
                <div>
                <button onClick={this.reloadPage}>Upload</button>
                </div>
                <Growl ref={(el) => { this.growl = el; }} /> 
             </form>
            </Panel >
        );

    }
}

export default FileUploadComponent

Sidofält.tsx

import * as React from 'react';
import { Link } from 'react-router-dom';
import 'nanoscroller';
import * as $ from 'jquery';
import * as classNames from 'classnames';
import { Menu } from 'primereact/components/menu/Menu';

interface AppProps { loadFileContent: (fileName: string) => any; }
interface AppState { 
    items: any; 
    loaded: boolean;
}

class Sidebar extends React.Component<AppProps, AppState> {

    scrollContainer: any;
    formItems = [];
    constructor(props: AppProps) {
        super(props);
        this.state = {
            items: [
                {
                    label: 'Admin',
                    items: [{label: 'New Template', icon: 'fa fa-plus-square', 
                            command: () => { window.location.href = '/FileUploadComponent'; }},
                            {label: 'List Template', icon: 'fa fa-list-ol', 
                            command: () => { window.location.href = '/CapitalHomelessness'; }}
                    ]
                }, 
                {
                    label: 'Form',
                    items: this.formItems
                }
            ],
            loaded: false
        };

        this.openMenu = this.openMenu.bind(this);
        this.getMenuItems = this.getMenuItems.bind(this);
    }
    // tslint:disable-next-line:typedef
    openMenu(event, val) {

        setTimeout(() => $(this.scrollContainer).nanoScroller(), 350);
        event.preventDefault();
    }

    componentDidMount() {
        // this.getMenuItems();
        $(this.scrollContainer).nanoScroller({ flash: true });

        // get all templates from Templates folder

    }

    componentWillMount() {
        // tslint:disable-next-line:no-console
        console.log('will mount');
        this.getMenuItems();
    }

    componentWillUnmount() {
        $(this.scrollContainer).nanoScroller({ destroy: true });
    }

    async getMenuItems() {

        const request = async () => {
            const response = await fetch('http://localhost:8000/file');
            const json = await response.json();
            json.files.forEach(async element => {
                var loadedContetnt = await this.props.loadFileContent(element);
                this.formItems.push({
                    label: element.split('.').slice(0, -1).join('.'), icon: 'fa fa-file',
                    data: loadedContetnt,
                    command: async () => {
                        // var loadedContetnt = await this.props.loadFileContent(element);
                        // tslint:disable-next-line:no-console
                        console.log(loadedContetnt);
                        window.location.href = '/FormLoading';
                    }
                });
            });
        };

        request();

        // fetch('http://localhost:8000/file', {
        //     method: 'GET'
        // }).then((response) => {
        //     response.json().then((body) => {
        //         body.files.forEach(element => {
        //             // tslint:disable-next-line:no-console
        //             console.log('command');
        //             this.formItems.push({
        //                 label: element.split('.').slice(0, -1).join('.'), icon: 'fa fa-file',
        //                 command: () => {
        //                     var loadedContetnt = this.props.loadFileContent(element);
        //                     // tslint:disable-next-line:no-console
        //                     console.log(loadedContetnt);
        //                    // window.location.href = '/FormLoading';
        //                 }
        //             });
        //         });
        //     });
        //     this.setState({ loaded: true });
        // });

        // tslint:disable-next-line:no-console
        console.log(this.state.items);
    }

    render() {
        return (
            <div ref={(el) => this.scrollContainer = el} className="nano">

                <div className="nano-content">
                    <div>
                        <Menu model={this.state.items} />
                    </div>
                    {/* <div className="layout-menu">
                        <div>
                            <Link className="fa fa-home" to="/">Home</Link>
                        </div>
                        <div>&nbsp;</div>
                        <div>
                            <Link className="fa fa-wpforms" to="/CapitalHomelessness">Rent Supplement Form</Link>
                        </div>
                        <div>
                            <Link className="fa fa-wpforms" to="/RentSupplement">Rent Supplement Form (New)</Link>
                        </div>
                        <div>
                            <Link className="fa fa-wpforms" to="/TransformationHousing">Trans. Housing Form (New)</Link>
                        </div>
                        <div>&nbsp;</div>
                        <div>
                            <Link className="fa fa-upload" to="/upload">Upload Form Template</Link>
                        </div>
                        <div>
                            <Link className="fa fa-database" to="/retrieve">Retrieve Form Template</Link>
                        </div>
                    </div> */}
                </div>
            </div>
        );
    }
}

export default Sidebar;