【问题标题】:useCallback does not update on dependency in specific circustamcesuseCallback 不会更新特定环境中的依赖关系
【发布时间】:2021-08-13 19:35:08
【问题描述】:

我有一个 select 函数,它使用 useCallback 使用单个依赖项进行初始化:name。然后我将此函数传递给一些嵌套组件,以便在图表元素上的click 上调用。如果我按照我的方式传递函数(参见下面的示例),结果证明 useCallback 的依赖关系 name 永远不会被识别(如果我通过输入新值来更新输入字段,console.log 会输出旧的 @ 987654328@ 当我点击图表元素时)。任何想法为什么 React 可能会有这种行为?这与类实例化和使用有关吗?问题是我尝试在没有ChartWrapperChart 的裸svg(没有d3)类实例化的情况下使用相同的逻辑 - 它工作正常。所以,问题出在ChartWrapperChart 的更新版本中,使用类而不是组件函数。欢迎任何想法。这是不起作用的最小应用程序的完整代码:

App.js:

import React, {useState, useCallback} from 'react';
import './App.css';
import ChartWrapper from './ChartWrapper';

function App() {

  const [name, setName] = useState("some_name")

  const nameChangeHandler = (event) => {
    setName(event.target.value)
    console.log("Updated name to " + event.target.value)
  }

  const select = useCallback((node) => {
    console.log("selected " + node + " for name " + name)
  }, [name])

  return (
    <div className="App">
      <ChartWrapper onSelect={select} /><br />
      Name: <input name="name" value={name} onChange={nameChangeHandler}/>
    </div>
  );
}

export default App;

ChartWrapper.js:

import React, { useRef, useState, useEffect } from 'react';
import Chart from './Chart';

const ChartWrapper = (props) => {
    const chartArea = useRef(null)
    const [chart, setChart] = useState(null)

    useEffect(() => {
        if (!chart) {
            setChart(new Chart(chartArea.current, props.onSelect))
        }
        else {
            chart.update(props.onSelect)
        }
    }, [chart, props.onSelect])

    return (
        <div className="chart-area" ref={chartArea}></div>
    )
}

export default ChartWrapper

Chart.js:

import * as d3 from 'd3';
import React from "react";

class Chart {
  constructor(element, onSelect) {
    let vis = this
    vis.nodes = [2, 4, 6]
    vis.circleSize = 30;
    vis.distanceBetweenCircles = 5;

    vis.svg = d3.select(element).append('svg')
      .attr('width', vis.nodes.length * (vis.circleSize + vis.distanceBetweenCircles))
      .attr('height', vis.circleSize)

    vis.update(onSelect)
  }

  update(onSelect) {
    let vis = this;
    
    const circlesEnter = vis.svg.selectAll('circle').data(vis.nodes).enter()
    circlesEnter.append('circle')
      .attr('fill', "gainsboro")
      .attr('stroke', "grey")
      .style('cursor', 'pointer')
      .attr('cx', (d, i) => vis.circleSize/2 + vis.distanceBetweenCircles*i + vis.circleSize*i) 
      .attr('cy', vis.circleSize/2) 
      .attr('r', vis.circleSize/2) 
      .on('click', (event, d) => onSelect(d))

    const textsEnter = vis.svg.selectAll('text').data(vis.nodes).enter()
    textsEnter.append('text')
      .attr('text-anchor', "middle")
      .attr('dominant-baseline', "middle")
      .attr('font-size', "12")
      .style('cursor', 'pointer')
      .attr('x', (d, i) => vis.circleSize/2 + vis.distanceBetweenCircles*i + vis.circleSize*i)
      .attr('y', vis.circleSize/2)
      .text(d => d)
  }
}

export default Chart;

【问题讨论】:

    标签: javascript reactjs d3.js


    【解决方案1】:

    问题在于在执行更新时使用 d3 的 enter() - 因为当时 3 个圈子已经存在,.enter() 之后的调用不会添加任何新圈子(并且所有这些 attr-s 和 onclick 都会被添加仅限新圈子)。

    您可以通过在构造函数中绘制整个 svg 来修复它,并且在更新中只更新点击功能。

    import * as d3 from 'd3';
    import React from "react";
    
    export default class Chart {
      constructor(element, onSelect) {
        let vis = this
        vis.nodes = [2, 4, 6]
        vis.circleSize = 30;
        vis.distanceBetweenCircles = 5;
    
        vis.svg = d3.select(element).append('svg')
          .attr('width', vis.nodes.length * (vis.circleSize + vis.distanceBetweenCircles))
          .attr('height', vis.circleSize)
    
        const circlesEnter = vis.svg.selectAll('circle').data(vis.nodes).enter()
        circlesEnter.append('circle')
          .attr('fill', "gainsboro")
          .attr('stroke', "grey")
          .style('cursor', 'pointer')
          .attr('cx', (d, i) => vis.circleSize / 2 + vis.distanceBetweenCircles * i + vis.circleSize * i)
          .attr('cy', vis.circleSize / 2)
          .attr('r', vis.circleSize / 2)
    
        const textsEnter = vis.svg.selectAll('text').data(vis.nodes).enter()
        textsEnter.append('text')
          .attr('text-anchor', "middle")
          .attr('dominant-baseline', "middle")
          .attr('font-size', "12")
          .style('cursor', 'pointer')
          .attr('x', (d, i) => vis.circleSize / 2 + vis.distanceBetweenCircles * i + vis.circleSize * i)
          .attr('y', vis.circleSize / 2)
          .text(d => d)
    
        vis.update(onSelect)
      }
    
      update(onSelect) {
        let vis = this;
    
        vis.svg.selectAll('circle')
          .on('click', (event, d) => onSelect(d))
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-23
      • 2016-11-01
      • 1970-01-01
      • 2015-12-28
      相关资源
      最近更新 更多