React Memo功能引起的问题:Uncaught Error: Element type is invalid: expected a string but got: object。

5
我有下面的函数组件:-
import React from 'react'
import { Dropdown } from 'semantic-ui-react'
const DropDownMenu= (props)=> {

const options = [
    { key: 'fruits', text: 'fruits', value: 'Fruits' },
    { key: 'vegetables', text: 'vegetables', value: 'Vegetables' },
    { key: 'home-cooked', text: 'home-cooked', value: 'Home-Cooked' },
    { key: 'green-waste', text: 'green-waste', value: 'Green-Waste' },
    { key: 'other', text: 'other', value: 'other' },

];

function onChangeHandler(e) {
  console.log(e.target.innerText);
  props.getCategoryValue(e.target.innerText);
};

return (
        <Dropdown placeholder='Category' fluid selection options={options} 
 onChange={onChangeHandler} />
    );

};

export default React.memo(DropDownMenu);

上述功能组件在其父组件sellForm.js中呈现如下:-
import React,{Component} from 'react'
import { Button, Form} from 'semantic-ui-react'
import AutoCompleteInput from '../GoogleAutocomplete/autoComplete';
import DropDownMenu from '../DropDown/DropDown';
import update from 'react-addons-update';
import './sellForm.css';
import PreviewImages from '../PreviewImage/previewUploadedImages';
import FileInput from '../FileInput/FileInput';

class sellForm extends Component{
constructor(props){
    super(props);
    //this.imageUpload = React.createRef();
    this.state={
        postID: '',
        title: '',
        description:'',
        category:'',
        price: '',
        amount: '',
        freshness: '',
        contact: '',
        location: '',
        timestamp: '',
        images: []
    }
}

getCategoryValue=(category)=>{
    this.setState({category: category})
};

getItemLocation=(locationObject)=>{
    this.setState({location: locationObject})
};

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

postButtonClickHandler=()=>{
    console.log(this.state)
    console.log(typeof (this.state.images[0].file))
    // send this info to firebase database
};

 handleImageUpload= (file)=>{
     console.log('handle image Upload in sell form');
     this.setState({
         images: update(this.state.images, {$push: [file]})
     })

 };

 handleImageDeletion=(indexOfImage)=>{
     console.log('handle image deletion in sell form - index to be deleted is : ' ,indexOfImage);
     this.setState((prevState)=>{
         return{
             // images: prevState.images.splice(indexOfImage,1)
             images: update(this.state.images, {$splice: [[indexOfImage,1]]})
         }
     })
 };

shouldComponentUpdate(nextProps,nextState){
    console.log('[sellform.js] shouldComponentUpdate');
    return true;
}

componentDidMount(){
    console.log('[sellform.js] componentDidMount');
}

static getDerivedStateFromProps(props, state){
    //when user uploads or deletes images, then props changes
    //this lifecycle executes when function gets new props before render()
    //only use when component's inner state depends upon props...
    console.log('[sellform.js] getDerivedStateFromProps')
    return null;
}
componentDidUpdate(prevProps){
    console.log('[sellform.js] componentDidUpdate')
}

componentWillUnmount(){
    console.log('[sellform.js] componentWillUmMount')
}

render(){
    console.log('render of sellForm');
    console.log(this.state.images);

    let previewImages = (<PreviewImages deleteUploadedImage={this.handleImageDeletion} images={this.state.images}/>)

    return(
        <Form>
            <Form.Field>
                <DropDownMenu getCategoryValue={this.getCategoryValue}/>
            </Form.Field>

            <Form.Field>
                {<AutoCompleteInput
                    onChange={()=>{}}
                    onPlaceSelected={this.getItemLocation}/>}
            </Form.Field>

            <Form.Field>
                <input
                    placeholder='What are you selling ?'
                    name="title"
                    onChange={this.saveInfo}/>
            </Form.Field>

            <Form.Field>
                <input
                    placeholder='Price'
                    name="price"
                    onChange={this.saveInfo} />
            </Form.Field>

            <Form.Field>
                    <FileInput appendImageToArray={this.handleImageUpload}/>
            </Form.Field>

            <Form.Field>
                <Button
                    type='submit'
                    onClick={this.postButtonClickHandler}>Post
                </Button>

            </Form.Field>

            <Form.Field>
                <div className='previewImageContainer'>
                    {previewImages}
                </div>
            </Form.Field>

        </Form>
    )
}
}



export default sellForm

当sellForm渲染时,会出现以下错误:未捕获的错误:元素类型无效:预期是字符串(用于内置组件)或类/函数(用于复合组件),但得到了:对象。

请检查sellForm的渲染方法。 在react社区有什么想法吗?


1
可以尝试将这一行改为:<AutoCompleteInput onChange={()=>{}} onPlaceSelected={this.getItemLocation}/>,去掉花括号。 - Said Kholov
@ShubhamKhatri... 我尝试过进行注释,如果在SellForm的render方法中注释掉<DropDown />,它可以正常工作。但是如果在SellForm的render方法中保留<DropDown />,会出现相同的错误。 - Manpreet Singh
@ShubhamKhatri.... 如果它能正常工作,那就完全没问题了。 - Manpreet Singh
我创建了一个沙盒并发现它在沙盒中运行良好,链接为https://codesandbox.io/s/o9ny76x4q6。 - Manpreet Singh
@Vernon 当我更新了React和React-DOM版本后,这些更改似乎没有立即生效...我不知道为什么...所以当我尝试用memo包装函数组件时,它会出现错误...Form是Semantic UI组件。如果这能澄清你的问题,请告诉我... - Manpreet Singh
显示剩余11条评论
2个回答

8

我通过将reactreact-dom升级到16.6.0来解决了这个问题。


我认为这是版本问题,但我使用 npm install --save react@^16.6.0 react-dom@^16.6.0 进行了更新... 它在本地主机上出现了问题... 当我创建共享沙盒时,它可以正常工作... - Manpreet Singh
最终它成功了...这是版本更新的问题。 - Manpreet Singh
更新React和DOM版本,它能正常工作,因为它是在React@16.6.0中引入的。 - Asif vora
太好了!仅更新React版本是不够的。 - nikksan

0

嘿,我认为问题是由于sellForm的命名方式。据我所知,React接受类的驼峰命名法。现在先看这个例子:

function Example() {
  // Declare a new state variable, which we'll call "count"
  return (
    <div>
      <p>Tes</p>
    </div>
  );
}
const MemoizedExample = React.memo(Example)
function exampleParent() {
  // Declare a new state variable, which we'll call "count"
  return (
    <div>
      <p>Parent</p>
       <MemoizedExample />
    </div>
  );
}

ReactDOM.render(<exampleParent />, document.getElementById("root"))
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>

在上面的代码中,我已经将类名小写化了,你可以看到渲染并没有发生。如果你将组件名称从exampleComponent改为ExampleComponent,它就能够工作了。类似地,解决你的问题只需要将类名sellFrom改为SellForm即可。这里是组件名称采用驼峰命名规则的正确代码:

function Example() {
  // Declare a new state variable, which we'll call "count"
  return (
    <div>
      <p>Tes</p>
    </div>
  );
}
const MemoizedExample = React.memo(Example)
function ExampleParent() {
  // Declare a new state variable, which we'll call "count"
  return (
    <div>
      <p>Parent</p>
       <MemoizedExample />
    </div>
  );
}

ReactDOM.render(<ExampleParent />, document.getElementById("root"))
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>


1
正如您建议的那样,我已经对SellForm进行了更改,但这并没有帮助...实际上,当我在父组件SellModal中呈现SellForm时,我已经遵循了CamelCasing,如下所示: import SellForm from './sellForm' https://stackoverflow.com/a/53057702/10578052 - Manpreet Singh
我是指尝试将sellForm组件的名称更改为SellForm组件。这是React.memo的非常简单的实现。对于我来说,很难看到其他任何错误。请仔细查看附在答案中的代码片段。 - simbathesailor
@simbathesailor....我确保使用了CamelCase命名规范....但错误仍然存在....根据React文档,这应该是非常直接的实现方式...但我不知道为什么它对我不起作用.... - Manpreet Singh
我相信 sellForm 本身没有问题,因为错误发生在其 render() 内部。 - skyboyer
@skyboyer,是的,你可能是对的。我认为需要更多的信息。还有其他的一些东西弄糊涂了。现在无法得出结论。 :) - simbathesailor
@skyboyer,@simbathesailor... 如果我在我的函数组件周围删除React.memo包装,它完全正常工作... 你认为父组件会以某种方式影响React.memo...这可能是问题...有什么想法吗?这是我的Github存储库的链接,其中包含完整的React代码... https://github.com/manpreetsjsu/cmpe133project/tree/manpreet - Manpreet Singh

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