反应路由器参数

Posted

技术标签:

【中文标题】反应路由器参数【英文标题】:React Router Params 【发布时间】:2020-10-02 20:02:21 【问题描述】:

晚上好,所以我正在尝试使用加载我的编辑页面,但我收到此错误 - 无法读取未定义的属性“参数”。

在我的编辑页面中,props.match.params._id 位于一个单独的提交组件中(在页面顶部),而不是导出的那个,然后在 App.js 上为路由导入。

正因为如此,我相信它导致了我的问题,但经过一些澄清后我不确定。

我确实将 props 传递给两个组件,我是否需要在 Edit 组件中的某个地方包含 props.match.params._id。

我担心 Edit 组件有点混乱,能否将组件从外部拆分出来,会有帮助吗?

编辑组件

import  MyTestStore  from "./App";
import  AppContext  from './context';
import "react-dropzone-uploader/dist/styles.css";
import Dropzone from "react-dropzone-uploader";
import styled from "styled-components";
import axios from "axios";
import  withRouter  from 'react-router-dom';

// import "./xApp.css";

import  Clear  from "@material-ui/icons";

// import  withRouter  from 'react-router-dom';
// import './style.css';

const MainContainer = styled.main`
  grid-area: main;
  display: grid;
  grid-template-columns: 1fr;
  margin: 0;
`;

const MovieForm = styled.form`
  display: grid;
  grid-template-columns: 1fr 25%;
  height: 100vh;
  width: 100%;
  margin: 0;
`;

const MovieInfo = styled.div`
  padding: 80px;
`;

const MoviePosters = styled.div`
  background: lightgrey;
  padding: 80px 40px;
`;

const HideMe = styled.div`
  height: 470px;
  overflow-y: auto;
  overflow-x: hidden;
  background: gainsboro;
  border: 1px solid lavender;
`;

const Submit = props => 
  const  files, onSubmit  = props;
  const  movie, setShowLoading, setMovie  = useContext(AppContext);
  const apiUrl = "http://localhost:5000/api/movies/" + props.match.params._id;

  useEffect(() => 
    setShowLoading(false);
    const fetchData = async () => 
      const result = await axios(apiUrl);
      setMovie(result.data);      
      console.log(result.data);
      setShowLoading(false);
    ;

    fetchData();
  , [apiUrl]);
  const handleSubmit = () => 
    console.log( movie );
    console.log(files.map(f => f.meta));

    const headers = "multipart/form-data";
    const formData = new FormData();
    formData.set("title", movie.title);
    formData.set("date", movie.date);
    formData.set("synopsis", movie.synopsis);
    formData.set("vID", movie.vID);
    formData.set("trailer", movie.trailer);

    files.map(fileItem => formData.append("poster", fileItem.file));

    console.log(Array.from(formData));
    axios
      .post(apiUrl, formData, headers)
      .then(result => 
        setShowLoading(false);
        console.log(result);
        props.history.push("/show/" + result.data.movie._id);
      )
      .catch(error => setShowLoading(false));
    onSubmit();
  ;
  return (
    <div className="dzu-submitButtonContainer">
      <button onClick=handleSubmit className="dzu-submitButton">
        Submit
      </button>
    </div>
  );
;

const Preview = ( meta, fileWithMeta ) => 
  const  previewUrl, name, status, percent  = meta;
  // const  uploadPercentage, setUploadPercentage  = useContext(AppContext);
  // const timeout = useRef();
  // console.log( status );
  // useEffect(() => 
  //   const time = 10000;
  //   if (uploadPercentage < 100) 
  //     timeout.current = setTimeout(() => 
  //       setUploadPercentage(uploadPercentage => uploadPercentage + 1);
  //     , time / 1000);
  //   

  //   return () => clearTimeout(timeout.current);
  // , []);
  // console.log(uploadPercentage);

  return (
    <div className="dzu-previewContainer">
      <img class="dzu-previewImage" src=previewUrl alt=name title=name />
      status === 'done' ? <Clear onClick=fileWithMeta.remove /> : ""
    </div>
  );
;

const Layout = (
  input,
  previews,
  submitButton,
  dropzoneProps,
  files,
  extra:  maxFiles 
) => 
  return (
    <div>
      <div>
        submitButton
        <HideMe>
          <div  ...dropzoneProps>
          previews
          files.length < maxFiles && input
          </div>
        </HideMe>
      </div>
    </div>
  );
;

const MyUploader = () => 
  return (
    <Dropzone
      autoUpload=false
      SubmitButtonComponent=Submit
      PreviewComponent=Preview
      LayoutComponent=Layout
      onSubmit=() => 
        console.log("After submit?");
      
      inputContent="Drop Files (Custom Layout)"
    />
  );
;

const Edit = (props) => 
  const  user, verified, setState  = useContext(MyTestStore);

  const  movie, setMovie, showLoading  = useContext(AppContext);

  return (
    // <AppContext.Provider value=values>

    <MainContainer>
      <MovieForm>
        <MovieInfo>
          showLoading && <span className="sr-only">Loading...</span>
          <h2 className="title">Add Movie</h2>
          <div className="wrapper">
            <span>Title</span>
            <input
              className="no-outline"
              type="text"
              name="title"
              defaultValue=movie.title
              onChange=e => setMovie( ...movie, title: e.target.value )
              placeholder="Film Title"
            />
          </div>

          <div className="wrapper">
            <span>Date</span>
            <input
              className="no-outline"
              type="date"
              name="date"
              defaultValue=movie.date
              onChange=e => setMovie( ...movie, date: e.target.value )
            />
          </div>

          <div className="wrapper">
            <span>Synopsis</span>
            <textarea
              className="no-outline"
              type="text"
              name="synopsis"
              defaultValue=movie.synopsis
              onChange=e => setMovie( ...movie, synopsis: e.target.value )
              placeholder="Synopsis"
            />
          </div>

          <div className="wrapper">
            <span>Movie ID</span>
            <input
              className="no-outline"
              type="text"
              name="vID"
              defaultValue=movie.id
              onChange=e => setMovie( ...movie, vID: e.target.value )
              placeholder="Veezi Film ID"
            />
          </div>

          <div className="wrapper">
            <span>Trailer</span>
            <input
              className="no-outline"
              type="text"
              name="trailer"
              defaultValue=movie.trailer
              onChange=e => setMovie( ...movie, trailer: e.target.value )
              placeholder="Trailer URL"
            />
          </div>
        </MovieInfo>

        <MoviePosters>
          <MyUploader />
        </MoviePosters>
      </MovieForm>
    </MainContainer>
    // </AppContext.Provider>
  );
;

export default withRouter(Edit);

应用程序路由

import  Route, Switch, Router  from "react-router-dom";
import  createBrowserHistory  from "history";
import  checkLoggedIn  from "../util/session";

import "./xApp.css";
// import "../script";

import  AuthRoute, ProtectedRoute  from "./Login";
import AddFilm from "./AddFilm";
import Movies from "./xMovies";
import Welcome from "./Welcome";
import Login from "./Login";
import Signup from "./Signup";
import Admin from "./xAdmin";
import Dashboard from "./xDashboard";
import AdminPage from "./xPage";
import EditPage from "./EditPage";
import Create from "./Create";
// import CreateForm from "./CreateForm";
import Edit from "./EditForm";
import Show from "./Show";
import List from "./List";
import Reviews from "./Reviews";
import BoxOffice from "./BoxOffice";
import Trash from "./Trash";
const history = createBrowserHistory();






export const MyTestStore = React.createContext()


export default ((props) => 
    const [state, setState] = useState()
    useEffect(() => 

      async function appCheckLoggedIn() 
        const result = await checkLoggedIn();
      if (result)  
      console.log(user, verified: true)
      console.log(result.session);
      const user = result.session
      setState(user, verified: true)
       else 
      console.log(verified: true)
      setState(verified: true)
      
      
      // Execute the created function directly

      appCheckLoggedIn();

    , [])

  const loggedIn = !!state.user



    return (
  <>
    <MyTestStore.Provider value=...state, setState>
      <Router history=history>
        <Switch>
          <Route exact path="/" component=Welcome />
          <Route path="/list" component=List />
          <AuthRoute path="/login" component=Login />
          <AuthRoute path="/signup" component=Signup />

          <ProtectedRoute exact path="/admin" component=Dashboard layout=AdminPage />
          <ProtectedRoute exact path="/admin/movies" component=Movies layout=AdminPage />
          <ProtectedRoute exact path="/admin/movies/new" component=Create layout=EditPage />
          <ProtectedRoute path="/admin/movies/edit/:_id" component=Edit layout=EditPage />
          <ProtectedRoute exact path='/admin/movies/show/:_id' component=Show layout=AdminPage />

          <ProtectedRoute exact path="/admin/reviews" component=Reviews layout=AdminPage />
          <ProtectedRoute exact path="/admin/box-office" component=BoxOffice layout=AdminPage />
          <ProtectedRoute exact path="/admin/trash" component=Trash layout=AdminPage />
          loggedIn && <Admin /> || null 
        </Switch>
      </Router>
    </MyTestStore.Provider>
  </>
))

快速路线

const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser')
const upload = require('../config/multer.config.js');
const app = express();
fs = require('fs-extra')
app.use(bodyParser.json());
app.use(bodyParser.urlencoded(extended: true))


// Load Movie model
const Movie = require('../models/movie');

// @route GET api/movies/test
// @trailer tests movies route
// @access Public
router.get('/test', (req, res) => res.send('movie route testing!'));

// @route GET api/movies
// @trailer Get all movies
// @access Public
router.get('/', (req, res) => 
  Movie.find()
    .then(movies => res.json(movies))
    .catch(err => res.status(404).json( nomoviesfound: 'No Movies found' ));
);

// @route GET api/movies/:id
// @trailer Get single movie by id
// @access Public
router.get('/:id', (req, res) => 
  Movie.findById(req.params.id)
    .then(movie => res.json(movie))
    .catch(err => res.status(404).json( nomoviefound: 'No Movie found' ));
);

// @route GET api/movies
// @trailer add/save movie
// @access Public
router.post('/', upload.array('poster'), (req, res) => 

  var filenames = req.files.map(function(file) 
    return file.path; // or file.originalname
  );

  var a = (filenames.findIndex(function(item)
    return item.indexOf("poster")!==-1;
  ));

  var b = (filenames.findIndex(function(item)
      return item.indexOf("slide")!==-1;
  ));

  console.log(filenames);
  // console.log(movieData);
  // console.log(req.body);

  // console.log(req.files);

  var date = new Date(req.body.date);
  // var isoString = date.toISOString();

  var movieData = 
    id: req.body.id,
    title: req.body.title,
    synopsis: req.body.synopsis,
    trailer: req.body.trailer,
    OpeningDate: date,
    poster:  filenames[a],
    slide: filenames[b]
  ;

  let movie = new Movie(movieData);
  movie.save()
    .then(() => res.json( movie, msg: 'New movie added successfully' ))
    .catch(err => res.status(400).json('Unable to add this movie'))
);

// @route GET api/movies/:id
// @trailer Update movie
// @access Public
router.post('/:id', upload.array('posters'), (req, res) => 
Movie.findByIdAndUpdate(req.params.id)
.then(movie => 

    movie.id = req.body.id,
    movie.title = req.body.title,
    movie.synopsis = req.body.synopsis,
    movie.trailer = req.body.trailer

  console.log(req.files);
  console.log(req.files.length);
  if (req.files.length > 0) 
    console.log('hi');
    var filenames = req.files.map(function(file) 
      return file.path; // or file.originalname
    );

    var a = (filenames.findIndex(function(item)
      return item.indexOf("poster")!==-1;
    ));

    var b = (filenames.findIndex(function(item)
        return item.indexOf("slide")!==-1;
    ));

    console.log(a);

    if (a >= 0) 
      movie.poster = filenames[a]
    

    if (b >= 0) 
      movie.slide = filenames[b]
    

  
  console.log(movie);
  movie.save()
  .then(() => res.json( movie, msg: 'Updated successfully' ))
  .catch(err =>
      res.status(400).json('Unable to update the Database'))
    )
    .catch(err =>
      res.status(400).json('Unable to update the Database'));
);

// @route GET api/movies/:id
// @trailer Delete movie by id
// @access Public
router.delete('/:id', (req, res) => 
  Movie.findByIdAndRemove(req.params.id)
    .then(() => res.json('Movie entry deleted successfully'))
    .catch(err => res.status(404).json( error: 'No such a movie' ));
);

module.exports = router;

【问题讨论】:

【参考方案1】:

假设 Edit 组件在其 props 中获取正确的值,您可以在 Edit 中移动 Submit 以共享相同的 props。所以像:


const Edit = (props) => 
  ...

  const Submit = () => 
    // here you'll have access to the props passed into Edit
    ...
  ;

  const Preview = (...) => 
    ...
  ;

  const Layout = (...) => 
    ...
  ;

  const MyUploader = () => 
    ...
  ;

  return (
    <MainContainer>
      ...
    </MainContainer>
  );
;

【讨论】:

以上是关于反应路由器参数的主要内容,如果未能解决你的问题,请参考以下文章

反应路由器查询参数字符串加载默认页面'/'

反应路由器参数化路由:SyntaxError:预期表达式,得到'<'

反应路由器不使用参数

反应路由器相同的路由不同的参数

反应路由器参数

可选的反应路由器参数