1

我有使用 axios 和 Redux 的 react-App。我fetchById在组件中使用了操作,courseDetails.jsx但状态没有更新为新值。由于使用组件从后端获取值需要一些时间,因此fetchById由于先前组件的旧状态(课程)而显示错误。redux-logger在下一个状态中显示正确的值,但组件未使用新状态更新。这是courseDetails.jsx组件代码。

import React, { useState, useEffect } from "react";
import {useDispatch,useSelector} from 'react-redux';
import { Link,useParams,useHistory } from 'react-router-dom';
import * as actions from "../_actions/courseActions";
import { makeStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import parse from 'html-react-parser';

const useStyles = makeStyles({
  title: {
    fontSize: 14,
    display: 'flex',
  },
  pos: {
    marginBottom: 12,
  },
  card: {
    padding:10,
    marginTop:10,
    display: 'flex',
    flex:1,
  },
  cardAction: {
    display: 'block',
    textAlign: 'initial'
  },
  cardMedia: {
    width: 160,
  },
  
});

export default function CourseDetails(props) {
 
  const [loading, setLoading] = useState(true);
  const classes = useStyles();
  let {courseId}=useParams();
  let {courseTitle}=useParams();
  let history = useHistory()
const dispatch = useDispatch();
useEffect(() => {
  //call the action creator with dispatch
  //  and wait until the promise resolves
  actions.fetchById(courseId)(dispatch)
     setLoading(false);
}, []);

let  single= useSelector(state=>state.course.list)
    //const course=myList.filter(x=>x.courseId==courseId);
   console.log("course in courseDetails",single)

  return (
    <div>
    {loading === true ? (<em>Loading!please wait...</em>) : (
    <Card className={classes.card} variant="outlined">
    <CardContent>
    <Typography variant="h5" component="h2">
    {single.courseTitle} 
      </Typography>
    <Typography variant="h5" component="h2">
    {single.subject} 
      </Typography>
      <Typography className={classes.title} color="textSecondary" gutterBottom>
       Fee: {single.fee}
      </Typography>
      
     
      <Typography variant="body2" component="p">
      {parse(single.details)}
        <br/>
        updated on: {single.updatedOn}
       
        </Typography>
      <Typography variant="subtitle1" color="primary">
      <div><br/>
      <Link to={`/CourseLessons/${courseId}`}
><Button size="small" variant="contained" color="secondary"> Free Lectures </Button> </Link>-----
      
 
      </div>
              </Typography>
           
    </CardContent>
   
    </Card>
    )}
    </div>
  );
}

旧状态显示组件中的课程列表lessons.jsxCourseDetails.jsx尝试渲染组件并希望使单个课程在线显示,let single= useSelector(state=>state.course.list)由于lessons旧状态的值,它会产生错误。 控制台截图在这里 这是代码coursActions.js

export const fetchById = (id) => dispatch => {
    courseApi.course().fetchById(id)
      .then(response => {
          
          dispatch({
              type: ACTION_TYPES.FETCH_BY_ID,
              payload: response.data
          })
      })
      .catch(err => console.log(err))
}

这是coursApi.js代码

import axios from "axios";
import config from 'config';
const baseUrl = `${config.apiUrl}/api/`

export default {

    course(url = baseUrl + `Courses/`) {
        return {
            fetchAll: () => axios.get(url),
            fetchById: id => axios.get(url + id),
            create: newRecord => axios.post(url, newRecord),
            update: (id, updateRecord) => axios.put(url + id, updateRecord),
            delete: id => axios.delete(url + id)
        }
    }
}

这是courseReducer.js代码。

import { ACTION_TYPES } from '../_constants';

const initialState = {
    list: []
    
}


 export const course=(state = initialState, action)=>{

    switch (action.type) {
     
        case ACTION_TYPES.FETCH_ALL:
            
            return {
                
                 ...state,
                list: [...action.payload]
            }
        case ACTION_TYPES.FETCH_BY_ID:
            return {
                ...state,
                list: [action.payload]
            }

        case ACTION_TYPES.CREATE:
            return {
                ...state,
                list: [...state.list, action.payload]
            }

       

        
            
        default:
            return state
    }
}

这是rootReducer中的combinedReducer

import { combineReducers } from 'redux';

import { authentication } from './authentication.reducer';
import { registration } from './registration.reducer';
import { users } from './users.reducer';
import {course} from '../_reducers/courseReducer';
import {lesson} from '../_reducers/lessonReducer';
import {student} from '../_reducers/studentReducer';
import {tutor} from '../_reducers/tutorReducer';
import {need} from '../_reducers/needReducer';
import {post} from '../_reducers/postReducer';
import { alert } from './alert.reducer';

export const rootReducer = combineReducers({
  authentication,
  registration,
  users,
  course,
  post,
  need,
  lesson,
  student,
  tutor,
  alert
 
})

export default rootReducer;

这是store代码

import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { createLogger } from 'redux-logger';
import {rootReducer} from '../_reducers';

const loggerMiddleware = createLogger();

export const store = createStore(
    rootReducer,
    applyMiddleware(
        thunkMiddleware,
        loggerMiddleware

    )
    //window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()

);
4

1 回答 1

0

它可能对任何人都有帮助。但可能是比我共享的更好的解决方法。我在之后根据setTimeout() 需要更新了状态并解决了我的问题。完整如下:fetchById useEffectcourseDetails.jsx

import React, { useState, useEffect } from "react";
import {useDispatch,useSelector} from 'react-redux';
import { Link,useParams,useHistory } from 'react-router-dom';
import * as actions from "../_actions/courseActions";
import { makeStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import parse from 'html-react-parser';

const useStyles = makeStyles({
  title: {
    fontSize: 14,
    display: 'flex',
  },
  pos: {
    marginBottom: 12,
  },
  card: {
    padding:10,
    marginTop:10,
    display: 'flex',
    flex:1,
  },
  cardAction: {
    display: 'block',
    textAlign: 'initial'
  },
  cardMedia: {
    width: 160,
  },
  
});

export default function CourseDetails(props) {
 const [flag,setFlag]=useState(false);
  const [loading, setLoading] = useState(true);
  const classes = useStyles();
  let {courseId}=useParams();
  let {courseTitle}=useParams();
  let history = useHistory()
const dispatch = useDispatch();

useEffect(() => {
  actions.fetchById(courseId)(dispatch)
  setTimeout(()=>{
  setFlag(true)
  },2000)
  setLoading(false);
    
}, []);

  const single=useSelector(state=>state.course.list)


console.log("course in courseDetails",single[0])
console.log("flag",flag)







  return (
    <div>
    {loading === false && flag===true ? (
    <Card className={classes.card} variant="outlined">
    <CardContent>
    <Typography variant="h5" component="h2">
    {single[0].courseTitle} 
      </Typography>
    <Typography variant="h5" component="h2">
    {single[0].subject} 
      </Typography>
      <Typography className={classes.title} color="textSecondary" gutterBottom>
       Fee: {single[0].fee}
      </Typography>
      
     
      <Typography variant="body2" component="p">
      {parse(single[0].details)}
        <br/>
        updated on: {single[0].updatedOn}
       
        </Typography>
      <Typography variant="subtitle1" color="primary">
      <div><br/>
      <Link to={`/CourseLessons/${courseId}`}
><Button size="small" variant="contained" color="secondary"> Free Lectures </Button> </Link>-----
      
<Link to={`/Enroll/${single[0].courseId}/${single[0].courseTitle}`}
><Button size="small" variant="contained" color="primary"> Enroll Now</Button>
  </Link><br/><br/>
 
      </div>
              </Typography>
              <Button   onClick={() => history.goBack()}
            size="small" variant="contained" color="secondary">
              back
            </Button>
              {/* <Link to={`/courses`}
><Button size="small" variant="contained" color="primary"> All courses</Button> </Link><br/>
               */}
    </CardContent>
   
    </Card>
    ):(<em>Loading!please wait...</em>) }
    </div>
  );
}

于 2020-12-22T05:17:15.677 回答