You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React子组件隐藏状态不更新:如何让RtSelect正常显示?

问题:RtSelect子组件未响应hidden状态更新

选择项目后,作为子组件的RtSelect隐藏状态未更新,本该显示所选项目的所有任务。getTasks异步请求完成后,已将hidden设为false并传给子组件props,但RtSelect始终不重新渲染,保持隐藏状态。需要修改哪些代码让它响应hidden变化?


父组件代码

import React, { useState, useEffect, useRef } from 'react';
import RtSelect from './RtSelect';
import api, { route } from "@forge/api";

function Projects() {
  const projRef = useRef();
  const taskRef = useRef();

  const [projects, setProjects] = useState(undefined)
  const [tasks, setTasks] = useState(undefined)
  const [projectid, setProjectid] = useState(undefined)
  const [taskid, setTaskid] = useState(undefined)
  const [hidden, setHidden] = useState(true)
  
  // 从Atlassian获取项目数组
  useEffect(() => {
    let loadedProject = true;

    const fetchProjects = async () => {
      const response = await api.asUser().requestJira(route`/rest/api/3/project`, {
        headers: {
          'Accept': 'application/json'
        }
      });
      const data = await response.json();
      const result = data.map(function (item) {
        console.log('test');
        return [
          {
            label: item.name,
            value: item.id,
            avatar: item.avatarUrls['16x16']
          }
        ]
      })

      if (loadedProject) {
        setProjects(result);
      }
    }

    fetchProjects()
      .catch(console.error);

    return () => loadedProject = false;
  }, [param])

  const getTasks = async (p) => {
    const response = await api.asUser().requestJira(route`/rest/api/3/issuetype/project?projectId={p}`, {
      headers: {
        'Accept': 'application/json'
      }
    });
    const data = await response.json();
    const result = data.map(function (item) {
      console.log('test');
      return [
        {
          value: item.id,
          label: item.description,
          avatar: item.iconUrl
        }
      ]
    })
    setTasks(result)
    setHidden(false)
  }

  useEffect(() => {
    projRef.current.addEventListener("onChange", (e) => {
      setProjectid(e.target.value)
      console.log("项目选择框的值为: " + e.target.value);
      getTasks(projectid)
    });
  });

  useEffect(() => {
    taskRef.current.addEventListener("onChange", (e) => {
      setTaskid(e.target.value)
      console.log("任务选择框的值为: " + e.target.value);
    });
  });
  
  return (
    <div>
      <div className='projects'>
        <RtSelect info="选择项目:" options={projects} hidden={false} ref={projRef} />
      </div>
      <div className='tasks'>
        <RtSelect info="选择任务:" options={tasks} hidden={hidden} ref={taskRef} />
      </div>
    </div>
  );
}

export default Projects

RtSelect子组件代码

import React from "react";
import Select from "react-select";

class RtSelect extends React.Component {
  state = {
    info: this.props.info,
    options: this.props.options,
    hidden: this.props.hidden,
    menuIsOpen: '',
    menuWidth: "",
    IsCalculatingWidth: ''
  };

  constructor(props) {
    super(props);
    this.selectRef = props.ref
   
    this.onMenuOpen = this.onMenuOpen.bind(this);
    this.setData = this.setData.bind(this);
  }
  componentDidMount() {
    if (!this.state.menuWidth && !this.state.isCalculatingWidth) {
      setTimeout(() => {
        this.setState({IsCalculatingWidth: true});
        this.selectRef.current.select.openMenu();
        this.setState({menuIsOpen: true});
        
      }, 1);
    }
  }

  onMenuOpen() {
    if (!this.state.menuWidth && this.state.IsCalculatingWidth) {
      setTimeout(() => {
        const width = this.selectRef.current.select.menuListRef.getBoundingClientRect()
          .width;
        this.setState({menuWidth: width});
        this.setState({IsCalculatingWidth: false});

        this.selectRef.current.select.onMenuClose();
        this.setState({menuIsOpen: undefined});
      }, 1);
    }
  }

  styles = {
    menu: (css) => ({
      ...css,
      width: "auto",
      ...(this.state.IsCalculatingWidth && { height: 0, visibility: "hidden" })
    }),
    control: (css) => ({ ...css, display: "inline-flex " }),
    valueContainer: (css) => ({
      ...css,
      ...(this.state.menuWidth && { width: this.state.menuWidth })
    })
  };
  setData (props) {
    if (props.info) {
      this.setState({
        info: props.info 
      })
    }
    if (props.options) {
      this.setState({
        options: props.options
      })
    }
    if (props.hidden) {
      this.setState({
        hidden: props.hidden
      })
    }
  }
  render () {
    return (
      <div style={{ display: "flex" }}>
        <div style={{ margin: "8px" }}>{this.state.info}</div>
        <div style={{minWidth: "200px"}}>
          <Select
            ref={this.selectRef}
            onMenuOpen={this.onMenuOpen}
            options={this.state.options}
            menuIsOpen={this.state.menuIsOpen}
            styles={this.styles}
            isDisabled={this.state.hidden}
            formatOptionLabel={(options) => (
              <div className="select-option" style={{ display: "flex", menuWidth: "200px"}}>
                <div style={{ display: "inline", verticalAlign: "center" }}>
                  <img src={options.avatar} width="30px" alt="Avatar" />
                </div>
                &nbsp;
                <div style={{ display: "inline", marginLeft: "10px" }}>
                  <span>{options.label}</span>
                </div>
              </div>
            )}
          />
        </div>
        </div>
    );
  }
}

export default RtSelect;

必须修改的代码点

1. 让RtSelect响应props变化

你把父组件传入的hidden、options等props存在了子组件自己的state里,但只在初始化时赋值,props更新时不会同步到state。有两种修复方式:

方式一:直接使用props(推荐)

删掉子组件state中info、options、hidden的初始化,所有用到这些值的地方直接用this.props替代:

// 移除state中的对应字段
state = {
  menuIsOpen: '',
  menuWidth: "",
  IsCalculatingWidth: ''
};

// render里修改对应部分
<div style={{ margin: "8px" }}>{this.props.info}</div>
// ...
<Select
  // ...
  options={this.props.options}
  isDisabled={this.props.hidden}
  // ...
/>

方式二:添加生命周期同步props到state

如果一定要保留state,给RtSelect添加componentDidUpdate方法:

componentDidUpdate(prevProps) {
  if (prevProps.hidden !== this.props.hidden) {
    this.setState({ hidden: this.props.hidden });
  }
  if (prevProps.options !== this.props.options) {
    this.setState({ options: this.props.options });
  }
  if (prevProps.info !== this.props.info) {
    this.setState({ info: this.props.info });
  }
}

2. 修复getTasks的参数错误

父组件中,setProjectid是异步更新,你调用getTasks(projectid)时,projectid还没更新成新值,应该直接传当前选中的id:

projRef.current.addEventListener("onChange", (e) => {
  const selectedId = e.target.value;
  setProjectid(selectedId);
  console.log("项目选择框的值为: " + selectedId);
  getTasks(selectedId); // 直接传选中值,而非state里的projectid
});

3. 给useEffect添加依赖数组

两个绑定事件的useEffect没有依赖数组,会导致每次渲染都重复绑定事件,引发冗余逻辑:

useEffect(() => {
  // 项目选择框的事件绑定代码
}, []); // 添加空数组,只绑定一次

useEffect(() => {
  // 任务选择框的事件绑定代码
}, []); // 添加空数组,只绑定一次

4. 修复ref传递错误

RtSelect中直接把props.ref赋值给内部的selectRef,会导致父组件的ref失效,应该用React.forwardRef正确传递:

// 修改RtSelect的导出方式
const RtSelect = React.forwardRef((props, ref) => {
  // 原组件代码,把this.selectRef换成ref即可
  // ...
});

export default RtSelect;

内容的提问来源于stack exchange,提问作者Jeroi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 17:06:00