组件不会在状态更改时重新呈现

时间:2019-03-05 09:10:50

标签: javascript reactjs

首先,我在此处查看了许多与此类似的问题,因此找不到解决方案。基本上在App.js中,我有两个按钮可以在英语和西班牙语之间切换this.state.language。当我对语言状态进行硬编码时,它将起作用。 (我通过JSON文件获取语言数据)。

但是,现在,当我尝试使用两个按钮时,状态会更改(通过控制台日志验证),但页面上的语言不会更新。在下面的页面上,我有一个if语句来检测语言状态,然后从中选择JSON文件中的位置来获取数据。请有人可以向我解释为什么这行不通吗?

App.js

class App extends Component {
  constructor(){
    super();
    this.state = {
      sideNav: '',
      language: ''
    }
    this.langEn = this.langEn.bind(this);
    this.langEs = this.langEs.bind(this);
  }

  langEn() {
    this.setState({language: 'en'});
    console.log(this.state);
  }

  langEs() {
    this.setState({language: 'es'});
    console.log(this.state);
  }

  render() {

    const mouseEnter = e => {
      this.setState({sideNav: "sideNav sidenav---sidenav---_2tBP sidenav---expanded---1KdUL"});
    }    

    const mouseLeave = e => {
      this.setState({sideNav: "sidenav---sidenav---_2tBP sidenav---collapsed---LQDEv"});
    }


    return (
      <div className="App container">
        <div>
          <SideNav 
            onMouseEnter={mouseEnter} 
            onMouseLeave={mouseLeave}
            className={this.state.sideNav}
            onSelect={(selected) => {
                // Add your code here
            }}
          >
            <SideNav.Nav  defaultSelected="home">
                <NavItem eventKey="home">
                    <NavIcon>
                        <Link to="/"><img src={Dash}/></Link>
                    </NavIcon>
                    <NavText>
                        <Link to="/">Dashboard</Link>
                    </NavText>
                </NavItem>
                <NavItem eventKey="sites">
                    <NavIcon>
                      <Link to="/sites"><img src={Site} /></Link>
                    </NavIcon>
                    <NavText>
                        <Link to="/sites">Sites</Link>
                    </NavText>
                </NavItem>
                <NavItem eventKey="tours">
                  <NavIcon>
                    <Link to="/tours"><img src={Tour}/></Link>
                  </NavIcon>
                  <NavText>
                      <Link to="/tours">Tours</Link>
                  </NavText>
                </NavItem>
                <NavItem eventKey="media">
                    <NavIcon>
                      <Link to="/media"><img src={Media}/> </Link>
                    </NavIcon>
                    <NavText>
                        <Link to="/media">Media</Link>
                    </NavText>
                </NavItem>
                <NavItem eventKey="newSite">
                    <NavIcon>
                        <Link to="/newSite/details"><img src={NewSite} /></Link>
                    </NavIcon>
                    <NavText>
                        <Link to="/newSite/details">Add new Site</Link>
                    </NavText>
                </NavItem>
                <NavItem eventKey="language">
                    <NavIcon>
                        <Link to="/language"><img src={Lang} /></Link>
                    </NavIcon>
                    <NavText>
                        <Link to="/language">Language</Link>
                    </NavText>
                </NavItem>
                <NavItem eventKey="profile">
                    <NavIcon>
                        <Link to="/profile"><img src={Profile} /></Link>
                    </NavIcon>
                    <NavText>
                        <Link to="/profile">Profile</Link>
                    </NavText>
                </NavItem>

            </SideNav.Nav>
            <button onClick={this.langEn}>EN</button>
            <button onClick={this.langEs}>ES</button>
          </SideNav>
        </div>
        <Routes childProps={this.state} />
      </div>
    );
      }
    }

    export default App;

我要翻译的页面:

import React, { Component } from 'react';
import { Col, Button, Form, FormGroup, Label, Input } from 'reactstrap';
import {withRouter} from 'react-router-dom';
import './NewForm.css';
import data from '../data.json';


class NewFormDetails extends Component {
    constructor(props) {
        super(props);

        this.state = {
            language: '',
            siteName: '',
            counties: '',
            siteAddress: '',
            siteEmail: '',
            siteNumber: '',
            siteCat: '',
            openTimes: '',
            fees: '',
            access: '',
            gps: '',
            w3w: '',
            txtHeader: '',
            txtContent: ''
        };

    }

    validateForm() {
        if (this.state.siteName != '' &&
            this.state.siteAddress != '' &&
            this.state.siteEmail != '' &&
            this.state.siteNumber != '' &&
            this.state.openTimes != '' && 
            this.state.fees != '' && 
            this.state.access != '' && 
            this.state.gps != '' && 
            this.state.w3w != '' && 
            this.state.txtHeader != '' && 
            this.state.txtContent != '') {
                return true;
            } else {
                return false;
            }
    }

    handleChange = e => {
        this.setState({ ...this.state, [e.target.name]: e.target.value });
        console.log(this.state);
    }

    handleSubmit = event => {
        event.preventDefault();
        console.log(this.state);
        this.props.history.push('/newSite/tours');
    }

    render() {

        this.setState({language: this.props.language});

        let jsonLang;

        if (this.state.language == 'en') {
            jsonLang = data.en;
        } else if (this.state.language == 'es') {
            jsonLang = data.es;
        } else {
            jsonLang = data.en;
        }

        this.placeholders = jsonLang.placeholders;
        this.counties = jsonLang.counties;
        this.categories = jsonLang.categories;
        console.log(this.state.language)

        return (
            <Form onSubmit={this.handleSubmit} className="form">
                {/* General Information */}
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="text" onChange={this.handleChange} name="siteName" id="siteName" placeholder={this.placeholders.siteName}/>
                    </Col>
                    <Col sm={6}>
                        <Input className="form-control" type="select" id="counties" onChange={this.handleChange}>
                            <option className="selectDefault" disabled value={this.placeholders.siteCounties} selected>{this.placeholders.siteCounty}</option>
                            { this.counties.map(c => (<option key={c.value} value={c.value}>{c.display}</option>))}
                        </Input>
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={12}>
                        <Input type="textarea" onChange={this.handleChange} name="siteAddress" placeholder={this.placeholders.siteAdd} id="siteAddress" />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="email" name="siteEmail" onChange={this.handleChange} id="siteEmail" placeholder={this.placeholders.email} />
                    </Col> 
                    <Col sm={6}>
                        <Input type="tel" name="siteNumber" onChange={this.handleChange} id="siteNumber" placeholder={this.placeholders.number}/>
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="select" name="siteCat" onChange={this.handleChange} id="siteCat" multiple placeholder={this.placeholders.categories}>
                            <option className="selectDefault" disabled selected>{this.placeholders.categories}</option>
                            { this.categories.map(c => (<option key={c.value} value={c.value}>{c.display}</option>))}
                        </Input>
                    </Col>
                    <Col sm={6}>
                        <Input type="textarea" name="openTimes" onChange={this.handleChange} id="openTimes" placeholder={this.placeholders.times} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={6}>
                        <Input type="textarea" name="fees" onChange={this.handleChange} id="fees" placeholder={this.placeholders.fees}/>
                    </Col>
                    <Col sm={6}>
                        <Input type="text" name="access" onChange={this.handleChange} id="access" placeholder={this.placeholders.access} />
                    </Col>
                </FormGroup>
                <hr/>
                {/* Location Information */}
                <FormGroup row> 
                    <Col sm={6}>
                        <Input type="text" name="gps" onChange={this.handleChange} id="gps" placeholder={this.placeholders.gps}/>
                    </Col>
                    <Col sm={6}>
                        <Input type="text" name="w3w" id="w3w" onChange={this.handleChange} placeholder={this.placeholders.w3w} />
                    </Col>
                </FormGroup>
                <hr/>
                <FormGroup row>
                    <Col sm={12}>
                        <Input type="textarea" name="txtHeader" onChange={this.handleChange} id="txtHeader" placeholder={this.placeholders.textHeader} />
                    </Col>
                </FormGroup>
                <FormGroup row>
                    <Col sm={12}>
                        <Input type="textarea" name="txtContent" onChange={this.handleChange} id="txtContent" placeholder={this.placeholders.textContent} />
                    </Col>
                </FormGroup>
                <FormGroup check row>
                    <Col sm={{ size: 10, offset: 2 }}>
                        <Button disabled={!this.validateForm()} type="submit" className="btn-primary">Tours &rarr;</Button>
                    </Col>
                </FormGroup> 
            </Form>
        );
    }
  }

  export default withRouter(NewFormDetails);

3 个答案:

答案 0 :(得分:1)

问题在于App从不使用this.state.language并且不会将其传递给任何其他组件。

如果要将语言设置为App级别,则至少有两个选择:

  1. 使用状态,并通过道具(传统的lifting state up)将language传递给需要状态的组件。
  2. 使用context
  3. 我确定还有使用hooks的第三个选项,但是我还没有陷入困境。

#1的简要示例:

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            language: "en"
        };
        this.langEn = this.langEn.bind(this);
        this.langEs = this.langEs.bind(this);
    }
    langEs() {
        this.setState({language: "es"});
    }
    langEn() {
        this.setState({language: "en"});
    }
    render() {
        const {language} = this.state;
        return (
            <div>
                <button onClick={this.langEn}>English</button>
                <button onClick={this.langEs}>Español</button>
                <Thingy language={language} />
                <Whatsit language={language} />
            </div>
        );
    }
}

class Thingy extends React.Component {
    render() {
        const {language} = this.props;
        return <div>Thingy's language is {language}</div>;
    }
}

const Whatsit = props => (
  <div>Whatsit's language is {props.language}</div>
);

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

第2个快速示例:

const LangContext = React.createContext("en");

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            language: "en"
        };
        this.langEn = this.langEn.bind(this);
        this.langEs = this.langEs.bind(this);
    }
    langEs() {
        this.setState({language: "es"});
    }
    langEn() {
        this.setState({language: "en"});
    }
    render() {
        const {language} = this.state;
        return (
            <LangContext.Provider value={language}>
                <div>
                    <button onClick={this.langEn}>English</button>
                    <button onClick={this.langEs}>Español</button>
                    <Thingy />
                    <Whatsit />
                </div>
            </LangContext.Provider>
        );
    }
}

class Thingy extends React.Component {
    static contextType = LangContext;
    render() {
        const language = this.context;
        return <div>Thingy's language is {language}</div>;
    }
}

class Whatsit extends React.Component {
    static contextType = LangContext;
    render() {
        const language = this.context;
        return <div>Whatsit's language is {language}</div>;
    }
}

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

答案 1 :(得分:-1)

正如注释中已经提到的那样,您没有在this.state.language的{​​{1}}方法中使用render()。只有在新状态实际上对NewFormDetails方法产生影响时,React才会重新渲染组件。

由于您只是在构造函数中使用它,因此react不会重新呈现您的组件。只需将if-else语句移至render方法,就可以了。

编辑:

您正在将render()复制到组件状态,但是此后没有更改状态。 this.props.language在外部发生了变化,但这些变化未反映在组件内部。您应该直接在this.props.language方法内部访问this.props.lanugage

render()

还是您有理由将其复制到州?

答案 2 :(得分:-1)

我注意到您从道具中获取了语言,并在NewFormDetails的构造函数中设置了转换后的字符串。当您单击按钮以更改语言时,NewFormDetails只是更新了,不需要执行构造函数,因此字符串是旧字符串。 我认为您可以从渲染功能中的道具获取语言,并在渲染功能中设置字符串。