【问题标题】:Setting a component state outside of the component results in error在组件之外设置组件状态会导致错误
【发布时间】:2018-04-10 08:41:50
【问题描述】:

构建一个模态组件,从应用程序的任何部分打开一个引导模态,然后在其外部为该组件设置自定义状态。它工作正常,但是一旦我打开模式,我总是会得到这个错误,我似乎无法弄清楚为什么:

警告:setState(...):在现有状态转换期间无法更新(例如在render 或其他组件的构造函数中)。渲染方法应该是 props 和 state 的纯函数;构造函数副作用是一种反模式,但可以移至componentWillMount。` 并没有真正破坏任何东西,但错误不断出现。

我的代码:

layout.js

import React from "react";
import {Link} from 'react-router';
import NotificationSystem from 'react-notification-system';

import AppHeader from "#/ui/header/AppHeader";
import AppFooter from "#/ui/footer/AppFooter";

import Modal from "#/ui/modals/modal/Modal";

import "@/main.scss";
import './layout.scss';


export default class Layout extends React.Component {
    constructor(props) {
        super(props);
    }

    componentDidMount() {
        app.notify.clear = this.refs.notificationSystem.clearNotifications;
        app.notify = this.refs.notificationSystem.addNotification;
        app.modal = this.refs.modal.updateProps;
    }

    render() {
        return (
            <div class="app">
                <div class="header">
                    <AppHeader page={this.props.location.pathname.replace('/', '')}/>
                </div>
                <div class="body">
                    {this.props.children}
                </div>
                <div class="footer">
                    <AppFooter />
                </div>

                <NotificationSystem ref="notificationSystem" style={false} />
                <Modal ref="modal" />
            </div>

        );
    };
}

Modal.js

import React from "react";
import ReactDOM from 'react-dom';

import SVGInline from "react-svg-inline";
import {closeSvg} from '#/utils/Svg';

export default class Modal extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            showHeader: true,
            showFooter: false,
            title: "",
            size: '',
            className: '',
            id: '',
            footerContent: null,
            showSubmitBtn: true,
            showCancelBtn: true,
            cancelBtnText: "Cancel",
            successBtnText: "Save Changes",
            onModalClose: () => {},
            showModal: false,
            html: () => {}
        }

        this.updateProps = this.updateProps.bind(this);
        this.hideModal = this.hideModal.bind(this);
    }

    componentWillMount() {
        var self = this;

        var $modal = $(ReactDOM.findDOMNode(this));
    }

    componentDidUpdate(prevProps, prevState) {
        if(this.state.showModal) {
            $('body').addClass('modal-open');
        } else {
            $('body').removeClass('modal-open');
        }
    }

    componentWillUnmount() {
        // $('body').removeClass("modal-open");
    }

    componentWillReceiveProps(nextProps) {
        console.log(nextProps);
    }

    updateProps(args) {
        let merged = {...this.state, ...args};
        this.setState(merged);
    }

    hideModal() {
        this.setState({
            showModal: false
        });

        this.state.onModalClose();
    }

    buildFooter() {
        if(this.props.footerContent) {
            return (
                <div class="content">
                    {this.props.footerContent}
                </div>
            )
        } else if(this.props.showCancelBtn && this.props.showSubmitBtn) {
            return (
                <div class="buttons">
                    <button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>{this.props.cancelBtnText}</button>
                    <button type="button" class="btn btn-success">{this.props.successBtnText}</button>
                </div>
            );
        } else if(this.props.showCancelBtn) {
            return (<button type="button" class="btn btn-default" data-dismiss="modal" onClick={this.props.onModalClose}>Close</button>);
        } else if(this.props.showSubmitBtn) {
            return (<button type="button" class="btn btn-success">Save changes</button>);
        }
    }

    render() {
        let {
            id,
            className,
            onModalClose,
            size,
            showHeader,
            title,
            children,
            showFooter,
            showModal,
            html
        } = this.state;

        return (
            <div class={`modal-wrapper`} >
                {
                    showModal ?
                        <div class={`modal fade in ${className}`} role="dialog">
                            <div class="bg" ></div>
                            <div class={`modal-dialog ${size}`}>
                                <div class="modal-content">

                                    { showHeader ?
                                        <div class="modal-header">
                                            <button type="button" class="close" data-dismiss="modal">
                                                <SVGInline svg={closeSvg} />
                                            </button>
                                            <h4 class="modal-title">{ title }</h4>
                                        </div> : '' }


                                    <div class="modal-body" >
                                        {html()}
                                    </div>

                                    {  showFooter ?
                                        <div class="modal-footer">
                                            { this.buildFooter() }
                                        </div> : ''
                                    }

                                </div>
                            </div>
                        </div>
                    : ''
                }
            </div>
        );
    }
}

SelectDefaultImage.js

import React from "react";
import sass from "./selectdefaultimage.scss";
import FullScreenImageModal from "#/ui/modals/fullscreenimagemodal/FullScreenImageModal";

export default class SelectDefaultImage extends React.Component {
    constructor() {
        super();

        this.state = {
            showModal: false,
            imgUrl: false,
        }
    }

    showImageModal(image) {
        this.setState({
            showModal: true,
            imgUrl: image
        });
    }

    hideImageModal() {
        this.setState({
            showModal: false,
            imgUrl: false
        })
    }

    onSelectImageClick(e, image) {
        $('.select-image-widget .active').removeClass('active');
        $(e.target).parent().addClass('active');

        // this.props.selectedImage(image)
    }

    render() {
        let {listingManager, images, selectedImage} = this.props;
        let {imgUrl} = this.state;

        return (
            <div class="content">
                <div class="row">
                    <div class="col-sm-12">
                        <label class="control-label" for="description">Select an Image</label>
                    </div>
                </div>

                <div class="row">
                    <div class="col-sm-12">
                        <div class="select-image-widget">
                            {
                                images.map((image, idx) => {
                                    return (
                                        <div class="selecter" key={idx}>
                                            <div class="img" style={{backgroundImage: `url(${listingManager.LISTINGS_PATH + image})` }} onClick={(e) => { this.onSelectImageClick(e, image) }}></div>
                                            <i class="fa fa-search-plus" aria-hidden="true" onClick={()=> {this.showImageModal(image)}}></i>
                                        </div>
                                    )
                                })
                            }
                        </div>
                    </div>
                </div>
                {
                    this.state.showModal ?
                        app.modal({
                            showModal: true,
                            className: "fullscreen-image-modal",
                            size: "modal-lg",
                            html: () => {
                                return (<img src={listingManager.LISTINGS_PATH + imgUrl} />);
                            }
                        })
                    : ''
                }
            </div>
        )
    }
}

【问题讨论】:

  • 问题中的代码似乎没有任何可能导致上述错误的问题。你把所有东西都包括进去了吗?另一方面,由于您正在导入外部库,因此此错误可能来自其中一个。
  • 虽然与问题没有直接关系,但尽量避免将jquery与react结合使用。如果你认为你需要 jquery,那意味着你没有以正确的方式做出反应。 reactjs.org/docs/thinking-in-react.html
  • @PubuduDodangoda 只有当 showModal 设置为 true 时,Modal.js 才会出现错误。仅在模态出现时发生。我已包含与此问题相关的所有代码。我被困住了,我已经坚持了 4 个小时了......
  • trolr 的目的是什么?
  • 对不起,也打算改一下app.modal,刚刚更新了我的问题

标签: javascript jquery twitter-bootstrap reactjs


【解决方案1】:

错误的原因很可能是在SelectDefaultImage 中,您从render 方法中调用了app.modal,而app.modalthis.refs.modal.updateProps,它执行了setState。如果您将app.modal 调用放入showImageModal,我希望错误会消失。但是,通过 refs 和 globals 设置另一个组件的状态有点 React 反模式,所以我建议进行一些重构并使用 props 来传递数据。

【讨论】:

  • 我支持这个。此外,您在该州拥有职能。也不推荐。
猜你喜欢
  • 2016-09-27
  • 2018-10-09
  • 2022-06-25
  • 2018-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-20
  • 1970-01-01
相关资源
最近更新 更多