类型错误:this.props.addNode不是一个函数。

4
任何我在这个组件中调用的动作似乎都会出现相同的错误,而我可以在另一个组件中调用相同的动作而没有问题。我已经检查了import、connect/router、参数、动作、路由,但我无法解决这个问题。特别是addNode动作。
以下是该组件:
import React, { Component } from 'react'
import {connect} from 'react-redux';
import {withRouter} from "react-router-dom";
import PropTypes from 'prop-types';
import "./AddPersonStyles.css";
import TextFieldGroup from "../common/TextFieldGroup";
import {addNode,getNodes} from "../../actions/nodeActions";
import {getTree,test} from "../../actions/treeactions";



class Interests extends Component {


    constructor(props) {
        super(props);
        this.state = {

           name: "",
           parentName: ""

        }
        this.onChange = this.onChange.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
        //this.props.test();
    }

    componentDidMount(){

    }

    onSubmit(e){
        e.preventDefault();

        const data = {
            name: this.state.name   
        }


        console.log(data.name, this.state.parentName );

        this.props.addNode("Books", "Cultural");
    }

    onChange(e) {

            this.setState({[e.target.name]: e.target.value});

    }





    render() {
        const { displayChildren} = this.state; 

        let children; 
        let listItems;

        if(!(this.props.tree2 === "" || undefined)){
            console.log(this.props.tree2.children);
            listItems = this.props.tree2.children.map((node) =>
                <div key = {node.name}>
                    <Interests key = {node.name} tree2 = {node} />
                </div>
            );
            console.log(listItems);
        }
        if(displayChildren){
            children = (
                <div className = "nodeContainer">
                {listItems}
                <form  className ="newChild" onSubmit = {this.onSubmit}>
                        <div className = "row derp"> 
                            <input

                                className = "form-control squish form-control-lg" 
                                placeholder="name"
                                name="name"
                                value = {this.state.name}
                                onChange = {this.onChange}
                                autoComplete = "off"
                            />             



                            <input type = "submit" value= "+" className = "btn btn-info butt"/>
                        </div>
                    </form> 
                </div>
            )
        }else{
            children = (

                    <div></div>     


            )
        }
        let buttonName = "test";
         if(!(this.props.tree2 === "")){
            console.log(this.props.tree2.name);
            buttonName = this.props.tree2.name;
            this.state.parentName = buttonName; 
        }   
        console.log("hello")
        return (
            <div>
                <div className = "d-flex nodeContainer flex-row ml-2 bd-highlight mb-2">

                    <button type = "button" className = "btn btn-info ml-2" onClick ={() => {
                            this.setState(prevState => ({
                                displayChildren: !prevState.displayChildren
                            }))
                        }}>
                        {buttonName}

                    </button> 
                </div>
                <div className = "col nodeStack">
                    {children}

                </div>


            </div>
        )
    }
}

Interests.propTypes = {
    //getNodes: PropTypes.func.isRequired,
    //node: PropTypes.object.isRequired
    //getTree: PropTypes.func.isRequired,
    //tree: PropTypes.object.isRequired,
    //test: PropTypes.func.isRequired 
    //addNode: PropTypes.func
    //errors: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
    //node: state.node
    //tree: state.tree
    //errors: state.errors
})


export default connect(mapStateToProps, {addNode, getNodes, getTree, test})(withRouter(Interests));

以下是动作创建器:

import { GET_ERRORS, GET_NODE} from './types';
import axios from 'axios';

export const addNode = (name, parent) => dispatch => {
    axios
        .post('/api/nodes/', {name, parent} )
        .then(res => console.log(res))
        .catch(err => 
            dispatch({
                type: GET_ERRORS,
                payload: err.response.data
            })
        );
}

export const getNodes = () => dispatch => {

    axios
        .get("/api/nodes/")
        .then(res =>
            dispatch({
                type: GET_NODE,
                payload: res.data
            }),

        )
        .catch(err =>
            dispatch({
                type: GET_NODE,
                payload: null
            })

        );
}

供参考,以下是我的另一个组件,在该组件中相同的操作可以正常工作。

import React, { Component } from 'react'
import {connect} from 'react-redux';
import {withRouter} from "react-router-dom";
import PropTypes from 'prop-types';
import TextFieldGroup from "../common/TextFieldGroup";
import TextAreaFieldGroup from "../common/TextAreaFieldGroup";
import SelectListGroup from "../common/SelectListGroup";
import {createProfile} from "../../actions/profileActions";
import Interests from "./Interests";
import{getTree,test} from "../../actions/treeactions";
import {addNode} from "../../actions/nodeActions";
import "./AddPersonStyles.css";



class AddPerson extends Component {

    constructor(props) {
        super(props);
        this.state = {

            name: "",
            sex: "",
            age: "",

            city: "",
            state: "",
            interests: "",
            bio: "",
            errors: {}

        }
        this.props.test();
        //this.props.addNode("Books", "Conceptual");
        this.onChange = this.onChange.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
        this.onSelectChange = this.onSelectChange.bind(this);
    }

    componentDidMount(){

    }

    componentWillReceiveProps(nextProps){

        if(nextProps.errors){
            this.setState({errors: nextProps.errors});
        }
    }
    onSubmit(e){
        e.preventDefault();

        const profileData = {
            name: this.state.name,
            sex: this.state.sex,
            age: this.state.age,
            city: this.state.city,
            state: this.state.state, 

            interests: this.state.interests,
            bio: this.state.bio
        }
        //console.log(profileData);
        this.props.createProfile(profileData, this.props.history);
    }

    onChange(e) {

            this.setState({[e.target.name]: e.target.value});

    }

    onSelectChange(e) {
        this.setState({status: e.target.value});
        console.log(this.status);
        console.log("derp");
    }

    render() {
        const {errors} = this.state; 

        const {tree} = this.props; 
        console.log(tree);
        console.log("here2");
        //select options for status
        const options = [

            {
                label: 'male',
                value: "male"
            },

            {label: 'female', value: "female"}
        ];



        return (
            <div className = "add-person">
                <div className = "container">
                    <div className = "row">
                        <div className = "col-md-8 m-auto">
                            <h1 className = "display-4 text-center">Add Person</h1>
                            <p className = "lead text-center">
                                enter info
                            </p>
                            <small className = "d-block pb-3">* = required fields</small>

                            <form  onSubmit = {this.onSubmit}>

                                <TextFieldGroup 
                                    placeholder = "name"
                                    name = "name"
                                    value = {this.state.name}
                                    onChange = {this.onChange}
                                    error = {errors.name}
                                    info = "name"
                                    autoComplete = "off"

                                />
                                <SelectListGroup 
                                    placeholder = "sex"
                                    name = "sex"
                                    value = {this.state.sex}
                                    onChange = {this.onChange}
                                    options = {options}
                                    error = {errors.sex}
                                    info = "sex"
                                />
                                <TextFieldGroup 
                                    placeholder = "age"
                                    name = "age"
                                    value = {this.state.age}
                                    onChange = {this.onChange}
                                    error = {errors.age}
                                    info = "age"
                                />
                                <TextFieldGroup 
                                    placeholder = "city"
                                    name = "city"
                                    value = {this.state.city}
                                    onChange = {this.onChange}
                                    error = {errors.city}
                                    info = "city"
                                />
                                <TextFieldGroup 
                                    placeholder = "state"
                                    name = "state"
                                    value = {this.state.state}
                                    onChange = {this.onChange}
                                    error = {errors.state}
                                    info = "state"
                                />


                                <TextFieldGroup 
                                    placeholder = "interests"
                                    name = "interests"
                                    value = {this.state.interests}
                                    onChange = {this.onChange}
                                    error = {errors.interests}
                                    info = "interests"
                                />

                                <TextAreaFieldGroup 
                                    placeholder = "bio"
                                    name = "bio"
                                    value = {this.state.bio}
                                    onChange = {this.onChange}
                                    error = {errors.bio}
                                    info = "bio"
                                />


                                <input type = "submit" value= "Submit" className = "btn btn-info btn-block mt-4"/>
                            </form>   

                        </div>
                    </div>
                </div>
                <br />
                <div className = "interests">
                    <div className = "row">
                        <div className = "col">
                             <br />

                            <h1 className = "display-4 text-center">Interests</h1>

                            <Interests tree2 = {tree.tree} />
                            {console.log(tree.tree)}
                            <div className = "container">
                            </div>
                        </div>
                    </div>
                </div>

            </div>
        )
    }
}

AddPerson.propTypes = {
    profile: PropTypes.object.isRequired,
    errors: PropTypes.object.isRequired,
    test: PropTypes.func.isRequired,
    tree: PropTypes.object.isRequired
}

const mapStateToProps = state => ({
    profile: state.profile,
    errors: state.errors,
    tree: state.tree
});

export default connect(mapStateToProps, {createProfile, addNode, test})(withRouter(AddPerson));

更新。我认为由于组件嵌套并且是递归的,就像树形层次结构一样,它会导致redux和mapstatetoprops出现问题。但还没有找到确切的原因或解决方法。 - Tyler Crawley
更新2。我更有信心这是问题所在。我理解将组件分为表示和容器组件的概念。https://redux.js.org/basics/usage-with-react https://howtoember.wordpress.com/2016/05/18/rendering-recursive-components-in-react-redux/ https://dev59.com/J5bfa4cB1Zd3GeqPyca3 - Tyler Crawley
显然,您正在呈现未连接并添加路由的<Interest>组件。递归嵌套组件不是问题,但是错误地嵌套它们是一个问题。在呈现子<Interest>组件时,您可以只传递{...this.props}。但是,从架构角度来看,我认为您应该使用两个组件-一个已连接和一个仅获取道具。已连接的那个不应该是递归的。 - Sulthan
1个回答

1
您之所以会出现错误,是因为您在普通的Interests组件上调用了props.addNodeprops.addNode仅在被导出的高阶组件中定义。 您错误地使用了未连接到connectwithRouter的普通组件:
...
listItems = this.props.tree2.children.map((node) =>
  <div key = {node.name}>
    <Interests key = {node.name} tree2 = {node} />
  </div>
);
...

当你应用“fluff”时,一切都会很好:
...
const InterestsWithFluff = connect(...)(withRouter(Interests))
listItems = this.props.tree2.children.map((node) =>
  <div key = {node.name}>
    <InterestsWithFluff key = {node.name} tree2 = {node} />
  </div>
);
...

实际上{...this.props} key = {node.name} tree2 = {node}可能已经足够了。 - Sulthan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接