Having troubles with delayed state changes due to setState being used within useEffect

I have been working on a slider effect using React Hooks and Redux, and here is the code I am using:

const Barchart = ({chartData}) => {
    let newArray = []
    let len = chartData.length

    const [XArray,setXArray]=useState([chartData])
    const [Yarray,setYArray]=useState(chartData[len-1].anArray)

    useEffect(()=>{
        let len = chartData.length
        console.log(chartData.length)
        newArray = chartData[len-1].anArray
        setYArray(newArray)

        if(newArray.length!==0){
        const height = 70 
        const width = 26.5*newArray.length 

        const svg = d3.select('.svg-canvas')
        svg.selectAll("*").remove()

        var x = d3.scaleLinear().domain([0,7]).range([0,width])
        var y = d3.scaleLinear().domain([0,d3.max(Yarray)]).range([height,0])

        var xAxis = d3.axisBottom(x).ticks(8)
        var yAxis = d3.axisLeft(y).ticks(5)

        var chartGroup = svg.append('g').attr('transform','translate('+(400 - width/2)+',300)')
        
        
        chartGroup.selectAll("rect").data(Yarray).enter().append("rect")
                .attr("height",(d,i)=>d*3)
                .attr("width","15")
                .attr("fill","blue")
                .attr('x',(d,i)=>26.5*i)
                .attr('y',(d,i)=>height-d*3)
                
        chartGroup.selectAll('text').data(Yarray).enter().append("text")
                .attr('font-size',15)
                .attr('x',(d,i)=>26.5*i)
                .attr('y',(d,i)=>height-5-d*3+2)
                .text((d,i)=>d)
        
        chartGroup.append('g').attr('class','axis y')
                .call(yAxis)

        chartGroup.append('g').attr('class','axis x')
                .attr('transform','translate(0,'+height+')')
                .call(xAxis)
        }
    },[chartData])

    const newArrayFunc = (a) =>{
        setYArray(a)
    }

    return(
        <div id='chart-container'>
            <h3>Bar Chart</h3>
            <svg className="svg-canvas" width="800px" height="400px"></svg>
        </div>
    )
}

const mapStateToProps = state => ({
    chartData:state.chartChange
});


export default connect(mapStateToProps)(Barchart)

Despite setting YArray in the useEffect, its asynchronous nature is causing the previous array to be used for the d3 bar chart instead of the updated one coming from chartData.

The main goal I'm aiming for is to utilize the updated array immediately in the d3 bar chart when there is a new array from chartData. How can I achieve this?

Answer №1

Option A

Continue utilizing the updated state value for newArray

const Barchart = ({ chartData }) => {
  let newArray = [];
  let len = chartData.length;

  const [XArray, setXArray] = useState([chartData]);
  const [Yarray, setYArray] = useState(chartData[len - 1].anArray); // Initial state should be an empty array here
  // const d3Container = useRef(null);

  useEffect(() => {
    let len = chartData.length;
    console.log(chartData.length);
    newArray = chartData[len - 1].anArray;
    setYArray(newArray);

    if (newArray.length) { 
      const height = 70; 
      const width = 26.5 * newArray.length; 

      const svg = d3.select(".svg-canvas");
      svg.selectAll("*").remove();

      var x = d3.scaleLinear().domain([0, 7]).range([0, width]);
      var y = d3
        .scaleLinear()
        .domain([0, d3.max(newArray)]) 
        .range([height, 0]);

      var xAxis = d3.axisBottom(x).ticks(8);
      var yAxis = d3.axisLeft(y).ticks(5);

      var chartGroup = svg
        .append("g")
        .attr("transform", "translate(" + (400 - width / 2) + ",300)");

      chartGroup
        .selectAll("rect")
        .data(newArray) 
        .enter()
        .append("rect")
        .attr("height", (d, i) => d * 3)
        .attr("width", "15")
        .attr("fill", "blue")
        .attr("x", (d, i) => 26.5 * i)
        .attr("y", (d, i) => height - d * 3);

      chartGroup
        .selectAll("text")
        .data(newArray) 
        .enter()
        .append("text")
        .attr("font-size", 15)
        .attr("x", (d, i) => 26.5 * i)
        .attr("y", (d, i) => height - 5 - d * 3 + 2)
        .text((d, i) => d);

      chartGroup
        .append("g")
        .attr("class", "axis y")
        .call(yAxis);

      chartGroup
        .append("g")
        .attr("class", "axis x")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
    }
  }, [chartData]);

  const newArrayFunc = (a) => {
    setYArray(a);
  };

  return (
    <div id="chart-container">
      <h3>Bar Chart</h3>
      <svg className="svg-canvas" width="800px" height="400px"></svg>
    </div>
  );
};

Option B

Modify the state and implement a separate effect to update d3

const Barchart = ({ chartData }) => {
  let newArray = [];
  let len = chartData.length;

  const [XArray, setXArray] = useState([chartData]);
  const [Yarray, setYArray] = useState(chartData[len - 1].anArray); // Initial state should be an empty array here
  // const d3Container = useRef(null);

  useEffect(() => {
    let len = chartData.length;
    console.log(chartData.length);
    newArray = chartData[len - 1].anArray;
    setYArray(newArray);
  }, [chartData]);

  useEffect(() => {
    if (Yarray.length) {
      const height = 70; 
      const width = 26.5 * Yarray.length; 

      const svg = d3.select(".svg-canvas");
      svg.selectAll("*").remove();

      var x = d3.scaleLinear().domain([0, 7]).range([0, width]);
      var y = d3
        .scaleLinear()
        .domain([0, d3.max(Yarray)])
        .range([height, 0]);

      var xAxis = d3.axisBottom(x).ticks(8);
      var yAxis = d3.axisLeft(y).ticks(5);

      var chartGroup = svg
        .append("g")
        .attr("transform", "translate(" + (400 - width / 2) + ",300)");

      chartGroup
        .selectAll("rect")
        .data(Yarray)
        .enter()
        .append("rect")
        .attr("height", (d, i) => d * 3)
        .attr("width", "15")
        .attr("fill", "blue")
        .attr("x", (d, i) => 26.5 * i)
        .attr("y", (d, i) => height - d * 3);

      chartGroup
        .selectAll("text")
        .data(Yarray)
        .enter()
        .append("text")
        .attr("font-size", 15)
        .attr("x", (d, i) => 26.5 * i)
        .attr("y", (d, i) => height - 5 - d * 3 + 2)
        .text((d, i) => d);

      chartGroup
        .append("g")
        .attr("class", "axis y")
        .call(yAxis);

      chartGroup
        .append("g")
        .attr("class", "axis x")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);
    }
  }, [Yarray]);

  const newArrayFunc = (a) => {
    setYArray(a);
  };

  return (
    <div id="chart-container">
      <h3>Bar Chart</h3>
      <svg className="svg-canvas" width="800px" height="400px"></svg>
    </div>
  );
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Stop users from being able to copy text on their smartphones' internet browsers

I am currently working on creating a competitive typing speed challenge using JavaScript. Participants are required to type all the words they see from a div into a textarea. In order to prevent cheating, such as copying the words directly from the div, o ...

Looking for a way to easily swipe through videos?

My mobile phone viewport displays a series of pictures and videos, with the swipeleft/right function enabled for browsing. However, I noticed that while the swipe feature works fine for images, it stops functioning when a video is displayed. Can anyone p ...

Having difficulty updating my npm package manager for node

When attempting to update my node package manager using the command npm install -g npm on my Windows system, I encountered an error that prevented me from updating successfully. PS C:\Users\LENOVO\Desktop\Toggle-Theme> npm install -g ...

What methods can be used to extend the distance measurement with the help of Google Maps

I've searched online for the answer, but still haven't found it. I'm currently developing a website where users can search and select a location using the Google Geocoding API. Once the user chooses a place, I retrieve its bounds, but then ...

Reorganize array of objects in JavaScript

So I am working with an array of objects structured like this: const data= [ { id: '6397f6f46b18bc89cb37053c', cost_center: null, plant: null, material: null }, { id: '6397f7166b18bc89cb372ff7', cost_center: &apo ...

Enhance the annotation of JS types for arguments with default values

Currently, I am working within a code base that predominantly uses JS files, rather than TS. However, I have decided to incorporate tsc for type validation. In TypeScript, one method of inferring types for arguments is based on default values. For example ...

NodeJs: Dealing with package vulnerabilities stemming from dependent npm packages - Tips for resolving the issue

Is there a way to address npm vulnerabilities that are dependent on another package? For instance, I'm encountering an error where the undici package relies on the prismix package. Things I have attempted: Executed npm audit fix Ensured Prismix is u ...

What methods could I use to prevent the WYSIWYG buttons from automatically linking?

I've been working on creating an editor but I'm facing a small issue. Every time I click on a button (such as bold or italic), it follows a link instead of performing the desired action. Here's a snippet of what I've tried so far: fu ...

Inconsistent behavior between Chrome and Firefox when using AngularJS $resource GET method: success in Chrome but error

I am currently working on a simple custom GET request in Angular using $resource angular.module('myApp') .factory('MyService', function($resource){ return $resrouce('some url', {}, { list: {method:'G ...

Showing hidden errors in specific browsers via JavaScript

I was struggling to make the code work on certain browsers. The code you see in the resource URL below has been a collection of work-around codes to get it functioning, especially for Android browsers and Windows 8. It might be a bit sketchy as a result. ...

Modifying the URL does not alter the selected tab

As part of my project, I wanted to ensure that when the page is refreshed, the previously selected tab remains active. To achieve this, I created a basic HTML page and added some jQuery. However, I encountered an issue when manually changing the URL from ...

When I incorporate a query, the value for this.props.location becomes undefined

Is there a way to add a query parameter like mobile=true to the URL in order to access a slightly modified page? I have successfully implemented this in App.js using the code const mobileAp = location.search.indexOf("mobile=true") > -1;. However, when I ...

What do you call the syntax %< ... >%?

Observed zoomInAnimation : true, zoomOutScale : false, templateLegend : "<ul class=\"<%=type.toLowerCase()%>-legend\"><% for (var j=0; j<sections.length; j++){%><li><span style=\"background-color:<%=section ...

I have been seeking the perfect solution to seamlessly incorporate ckeditor5 with comments in my AngularJS applications. Despite extensive research, I have not come across any angularjs-specific plugins for this purpose. It

import Comments from '@ckeditor/ckeditor5-comments/src/comments'; ClassicEditor.builtinPlugins = [ Essentials, Paragraph, Bold, Italic, Image, Comments ]; I am trying to figure out how to incorporate comments into the CKEditor5 in an AngularJS ...

Can someone please explain how I can implement a multi-submenu feature using JavaScript?

HTML CODES: <header> <nav> <ul> <li class="asmenu"><a href="#">Menu1 <i class="fa fa-caret-down"></i></a> <ul class="submenu deact ...

What is the method to select a hyperlink that includes a variable in the "href" attribute and click on it?

Currently, I am in the process of creating acceptance tests utilizing Selenium and WebdriverIO. However, I have encountered a problem where I am unable to successfully click on a specific link. client.click('a[href=#admin/'+ transactionId + &apo ...

What is the best way to display JSON data in a readable format?

I received a JSON file with the following structure: { "data":{ "uuid":"123", "name":"TestData", "alias":null, "created_at":"2021-03-17T11:57:29.000000Z&q ...

When URL string parameters are sent to an MVC controller action, they are received as null values

Are You Using a Controller? public class MyController : Controller { [HttpGet] public ActionResult MyAction(int iMode, string strSearch) { return View(); } } Within my view, I have a specific div with the id of "center" I am runn ...

Checking if a start date comes after an end date using Yup - how is it done?

How can I use Yup to validate if the start date overlaps with the end date in a form that creates an event using Formik library? I have two date pickers for choosing the dates and times. I want to display an error message if they overlap. Thanks for the ...

When the Protractor configuration is executed, it displays the message "The requested webpage cannot be accessed."

Testing protractor on a vanilla.js app and encountering an error when running protractor basicConf.js The following error is occurring: This webpage is not available ERR_CONNECTION_REFUSED Here is the test script in use: describe('foo', fun ...