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

React子组件向父组件传props报错:props.setTagsinput不是函数

问题分析与解决方案

错误根源

  1. props.setTagsinput is not a function 报错原因:
    Student.js 中使用 <TagsInput /> 时未传递 setTagsinput 属性,导致该组件实例的 props 中不存在这个函数,调用时触发错误。
  2. 潜在无限重渲染风险:
    TagsInput.js 中直接在组件顶层调用 props.setTagsinput(tags),会在每次组件渲染时执行,可能引发无限重渲染循环。

分步解决

1. 修复 TagsInput 组件的调用逻辑

修改 TagsInput.js,仅在 setTagsinput 存在时调用,且通过 useEffect 监听 tags 变化,避免无效重复调用:

import React from "react";

const TagsInput = (props) => {
    // 支持接收初始标签,适配不同场景
    const [tags, setTags] = React.useState(props.initialTags || []);

    // 仅在tags变化且setTagsinput存在时,向父组件传递数据
    React.useEffect(() => {
        if (typeof props.setTagsinput === 'function') {
            props.setTagsinput(tags);
        }
    }, [tags, props.setTagsinput]);

    const addTags = event => {
        if (event.key === "Enter" && event.target.value.trim() !== "") {
            setTags([...tags, event.target.value.trim()]);
            event.target.value = "";
        }
    };
    const removeTags = index => {
        setTags(tags.filter((_, idx) => idx !== index));
    };

    return (
        <div className="tags-input">
            <div className="tags_section">
                {tags.map((tag, index) => (
                    <div className="tag tag-flex" key={index}>
                        <p className="tag-p">{tag}</p>
                        {/* 补充删除按钮逻辑,完善交互 */}
                        <button onClick={() => removeTags(index)}>×</button>
                    </div>
                ))}
            </div>
            <input
                type="text"
                onKeyUp={addTags}
                placeholder="Press enter to add tags"
            />
        </div>
    );
};
export default TagsInput;

2. 处理 Student 组件中的 TagsInput 实例

如果每个学生需要独立维护标签并同步到 Top 组件,修改如下:

Top.js:添加学生标签状态管理

import React, {useState, useEffect} from 'react';
import axios from 'axios';
import Student from './Student';
import TagsInput from "./TagsInput";

const Top = () => {
  const [ posts, setPosts] = useState([]);
  const [ allPosts, setAllPosts] = useState([]);
  const [searchKeyword, setSearchKeyword] = React.useState("");
  const [searchTagKeyword, setTagSearchKeyword] = React.useState("");

  // 初始化时为每个学生添加tags属性
  useEffect(() => {
    axios.get('xxx.com')
    .then(result => {
      const studentsWithTags = result.data.students.map(student => ({
        ...student,
        tags: []
      }));
      setPosts(studentsWithTags);
      setAllPosts(studentsWithTags);
      if (searchKeyword) {
        getSearchResult()
      }
    })},
    [searchKeyword]);

  // 更新指定学生的标签
  const updateStudentTags = (studentId, newTags) => {
    setPosts(prev => prev.map(student => 
      student.id === studentId ? {...student, tags: newTags} : student
    ));
    setAllPosts(prev => prev.map(student => 
      student.id === studentId ? {...student, tags: newTags} : student
    ));
  };

  const getSearchResult = () => {
    const result = allPosts.filter(output => {
      const fullName = `${output.firstName} ${output.lastName}`.toLowerCase();
      return fullName.includes(searchKeyword.toLowerCase());
    });
    setPosts(result);
  };

  const getTagSearchResult = () => {
    const result = allPosts.filter(output => 
      output.tags.some(tag => tag.toLowerCase().includes(searchTagKeyword.toLowerCase()))
    );
    setPosts(result);
  };

  // 监听标签搜索关键词变化,触发搜索
  useEffect(() => {
    getTagSearchResult();
  }, [searchTagKeyword]);

  return (
    <div>
      {/* 顶部全局标签输入(如果需要) */}
      <TagsInput setTagsinput={(tags) => console.log('全局标签:', tags)} />
      <div>
        <input className="search-box" placeholder="搜索姓名" value={searchKeyword} onChange={(e) => setSearchKeyword(e.target.value)}/>
      </div>
      <div>
        {/* 修复标签搜索的onChange绑定错误 */}
        <input className="search-box" placeholder="搜索标签" value={searchTagKeyword} onChange={(e) => setTagSearchKeyword(e.target.value)}/>
      </div>
      <div>
        {searchKeyword && <p>{searchKeyword} 搜索结果</p>}
        {posts.length > 0 ?
          posts.map((data) =>
            <Student 
              key={data.id} 
              data={data} 
              updateTags={(newTags) => updateStudentTags(data.id, newTags)} 
            />
          )
          :
          <div><p>未找到匹配结果!</p></div>
        }
      </div>
    </div>
  );
}
export default Top;

Student.js:传递标签更新函数

import React, {useState} from 'react';
import TagsInput from './TagsInput';

const Student = (props) => {
  const [show, setShow] = useState(false);

  const gradesAverage = (grades) => {
    const sum = grades.reduce((acc, score) => acc + Number(score), 0);
    return (sum / grades.length).toFixed(1);
  };

  return (
    <div className="flex">
        <div className="image">
            <img src={props.data.pic} className="profile" alt={`${props.data.firstName} ${props.data.lastName}`} />
        </div>
        <div>
            <p className="name">{props.data.firstName} {props.data.lastName}</p>
            <button className="button" onClick={() => setShow(!show)}>
                {show? '-' : '+'}
            </button>
            <div className="info">
                <p>Email: {props.data.email}</p>
                <p>Company: {props.data.company}</p>
                <p>Skill: {props.data.skill}</p>
                <p>Average Grade: {gradesAverage(props.data.grades)}%</p>
                {show && (
                    <div>
                        {props.data.grades.map((score, idx) => (
                            <p key={idx}>Test {idx+1}: {score}%</p>
                        ))}
                    </div>
                )}
                {/* 传递初始标签和更新函数 */}
                <TagsInput 
                  initialTags={props.data.tags} 
                  setTagsinput={props.updateTags} 
                />
            </div>
        </div>
    </div>
  );
}
export default Student;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 17:51:31