简体   繁体   中英

Node and React are not in sync

I was able to achieve the following -> When a user clicks on a particular date in component A the data gets sent to the Node (Sails API) where all the necessary calculations are done, and before component B is rendered the correct data is ready to be shown.

The problem is when a user returns back from component B to component A and chooses a different date, he/ she gets the exact same result (old value) because even though the new value is sent to the backend API, Node isn't doing the recalculations with the new value.

I'm only able to achieve the correct result after I manually refresh the page, or make changes to the server so it forces the recalculation.

I think I need to mention that I'm passing data using Redux, so maybe the issue occurs on that part.

I would consider some type of auto refresh, animated loading, anything. Yup, so stuck :/

Is it even possible to make them in total sync?

UPDATE --> Here is the code:

BACKEND

getDetails: (req, res) => {
    authentication.authenticate().then((auth) => {
      const sheets = google.sheets('v4');
      sheets.spreadsheets.values.get({
        auth: auth,
        spreadsheetId: config.spreadsheetSettings.spreadsheetId, // id of spreadsheet
        range: config.spreadsheetSettings.employeeSheetId, // name of employee spreadsheet and range- get all cells
      }, (err, response) => {
        if (err) {
          res.serverError(err);
          return;
        }
        const rows = response.values; // response-all cells
        const updatedData = employeeService.mapEmployeeSheetToJson(rows);

         // FETCHING THE VALUE FROM REST API
        let myArr = [];

        (function() {

                    axios.get(`http://localhost:1337/api/`)
                    .then(res =>  {
                    let kajmak = res.data.slice(-1)[0]
                    let test = kajmak[Object.keys(kajmak)[0]]
                    myArr.push(test)
                    }).catch(err => console.error(err));
             })();

        // MAPING OVER THE ARRY AND DOING THE LOGIC
        setTimeout(() => {
            myArr.map(xo => {

        const result = [];

    updatedData.forEach(emp => {//  2013     2012  2014
        if (xo > parseInt(moment(emp.startdate).format('YYYYMM'), 10) &&
          (xo < parseInt(moment(emp.enddate).format('YYYYMM'), 10))) {
              result.push(emp);
          }
      });

      // IF THEY STARTED WORKING BEFORE THE SELECTED DATE AND STILL WORKING
    updatedData.forEach(emp => { // 2013   > 2012 & 2013   -
          if (xo > parseInt(moment(emp.startdate).format('YYYYMM'), 10) && 
              ((parseInt(moment(emp.enddate).format('YYYYMM'), 10) == undefined ))) {
                  result.push(emp);
          }
      });

      // IF THEY STARTED WORKIG BEFORE THE SELECTED DATE,
      // BUT STOPPED WORKING BEFORE THE SELECTED DATE
    updatedData.forEach(emp => {  // 2013  <    2014 ||     2013  > 2017
          if (xo < parseInt(moment(emp.startdate).format('YYYYMM'), 10) &&
              (xo > parseInt(moment(emp.startdate).format('YYYYMM'), 10))) {
                  result.pop(emp);
          }
      });

        // Getting the names to use for unique sheet req
        let finalResult = [];
        result.map(x => {
            finalResult.push((x.name + ' ' + x.surname))
        })

        if (rows.length === 0) {
          res.err('No data found.');
        } else {
          res.ok(finalResult);
        }
    }) 
        }, 1000);
    });

}

FRONTEND

getEmployeeSalaryData = () => {
            // GETTING THE CLICKED VALUE FROM THE PREVIOUS COMPONENT
            const { year } = this.props.history.location.state.item;
            const { month } = this.props.history.location.state.item;
            const selectedMonth = moment().month(month).format("MM");
            const finalSelect = parseInt(year + selectedMonth, 10);
            const { employees } = this.props;
            // I'M RECIEVING THIS AS PROPS USING REDUX AND THIS IS THE ACTUAL 'FINAL' DATA USED FOR FURTHER CALCS AND RENDERING
            const { details } = this.props;

             // HERE I'M SENDING THE 'CLICKED' VALUE FROM THE PREVIOUS COMPONENT TO THE BACKEND API
            axios.post(`http://localhost:1337/api/`, { 'test' : finalSelect })
                .then(res => {
                console.log('Data send')
                // console.log(res.data);
              }).catch(err => console.error(err));



            // Making the req 
            details.map(x => {

                EmployeeApi.getEmployee(x)
                    .then(y => {
                        //Making sure everything is in the right order
                        let test = Object.assign(y.data);
                        let ii = x;

                    setTimeout(
                        this.setState(prevState => ({
                            ...prevState.currentEmployee,
                            fullNames: [...prevState.currentEmployee.fullNames, ii]
                        })), 100);

                let onlyRelevantDate = [];
                test.map(item => {
                    if (finalSelect == parseInt(item.year + moment().month(item.month).format("MM"), 10)) {
                            onlyRelevantDate.push(item)
                        }})
                            this.setState(prevState => ({
                            currentEmployee: {
                            ...prevState.currentEmployee,
                            salaryInfo: [...prevState.currentEmployee.salaryInfo, onlyRelevantDate],
                            fullNames: [...prevState.currentEmployee.fullNames, ii]
                    }}))         
                })
            });
        }   
        componentWillReceiveProps(nextProps) {
            this.getEmployeeSalaryData(nextProps);
        }

        componentWillMount() {
            this.getEmployeeSalaryData(this.props);
        }

In component A you should dispatch an action that is a function taking a dispatch function.

//some click handler for when user makes a selection
//  the function should be in action creator file but you get the jist
const handleSomeClick = someValue =>
  //when you dispatch an action that is a function in redux with thunk then
  //  the thunk middleware will not call next (no reducers will be called)
  //  thunk will pass a parameter to this function that is the dispatch
  //  function so from your function you can dispatch actual object action(s)
  dispatch(
    dispatch=>
      setTimeout(
        dispatch({type:"changedValue",data:someValue}),//dispatching the action
        someValue*1000//assuming someValue is a number
      )
  )

Here is an example that has component A set someValue depending on what button is clicked and will highlight that button it'll also set someValue of B asynchronously. This is done in the function changeLater that dispatches an action that is a function so thunk will execute it with the dispatch.

This function will dispatch an action after a timeout. If you click the numbers 5 and then 1 (quickly) you'll see that the highlighted button of A and value after async of B do not match (highlighted of A is 1 and value after async of B is showing 5).

This is because the order of which the user clicks and starts the async process is not the same as the order the async process resolves. You could solve this by only dispatching an action when it's the last resolved promise.

This example shows how it's done by using a promise created by later and only resolve it if it's the last by using a partially applied version of onlyLastRequestedPromise called lastNumberClicked

您可以使用RxJS解决此问题

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM