反应 - 在无状态组件中切换

时间:2018-03-19 16:07:04

标签: javascript reactjs

我试图在像这样的无状态组件中切换div的可见性:

const playerInfo = (props) => {
  let isPanelOpen = false;
  return (
      <div onClick={() => isPanelOpen = !isPanelOpen }>Toggle</div>
      {isPanelOpen && <div className="info-panel">
        {this.props.children}
      </div>}
  );
};

我发现isPanelOpen的值更改为true,但面板未显示。我假设这是因为这是无法再次调用的无状态函数,所以一旦我们返回jsx,它将具有false的值,并且稍后不会更新它。 有没有办法解决这个问题,并避免通过另外4个父无状态组件来修改这个单个变量作为道具?

6 个答案:

答案 0 :(得分:5)

您无法通过直接为变量分配新值来告诉React重新呈现UI(在您执行isPanelOpen = !isPanelOpen的情况下)。
正确的方法是使用setState

但你无法在无状态组件中执行此操作,必须在有状态组件中执行此操作,因此您的代码应如下所示

import React, {Component} from 'react';
class playerInfo extends Component {
    constructor(props){
        super(props);
        this.state = {
            isPanelOpen: false
        }
    }
    render() {
        return (
            <div onClick={() => this.setState({isPanelOpen: !this.state.isPanelOpen})}>Toggle</div>
            {this.state.isPanelOpen && <div className="info-panel">
              {this.props.children}
          </div>}
        );
    }
}

说明

记住两件事:
1)您的UI只应绑定到this.state.XXXX(对于有状态组件)或props.XXX(对于无状态组件)。
2)更新UI的唯一方法是调用setState()方法,没有其他方法会触发React重新呈现UI。

但是......如何更新无状态组件,因为它没有setState方法?

ONE ANSWER :无状态组件应该包含在另一个有状态组件中

让我们说你的无状态组件被称为Kid,你有另一个名为Mum的有状态组件。

import React, {Component} from 'react';
class Mum extends Component {
    constructor(props){
        super(props);
        this.state = {
            isHappy: false
        }
    }
    render() {
        return (
           <div>
                <button onClick={() => this.setState({isHappy: true})}>Eat</button>
                <Kid isHappy={this.state.isHappy}/>
           </div>
        );
    }
}

const Kid = (props) => (props.isHappy ? <span>I'm happy</span> : <span>I'm sad</span>);

答案 1 :(得分:1)

  

我认为这是因为这是无法再次调用的无状态函数

基本上,重新渲染组件的唯一方法是更改​​状态或道具。 :)

因此,当您更改本地变量时,React不会收到有关它的通知,也不会启动reconcilation

答案 2 :(得分:1)

您可以使用本机Javascipt执行此操作,否则在React中您无法使用无状态组件执行此操作:)

const playerInfo = (props) => {
    let isPanelOpen = false;
    return ( <
        div onClick = {
            () => {
                if (document.getElementsByClassName("info-panel")[0].style.display == 'none') {
                    isPanelOpen = true;
                    document.getElementsByClassName("info-panel")[0].style.display = '';
                } else {
                    isPanelOpen = false;
                    document.getElementsByClassName("info-panel")[0].style.display = 'none';
                }
            }
        } > Toggle < /div> <
        div className = "info-panel" > {
            this.props.children
        } <
        /div>
    );
};

答案 3 :(得分:1)

您可以通过使用useState挂钩来做到这一点:

import { useState } from "react";

            function playerInfo () {

        const [panel, setPanel] = useState(false);

        function toggleButton () {
            if(!panel) setPanel(true);
            else setPanel(false);
            }

        return (
            <div>
            <button onClick={toggleButton}>Toggle</div>
            panel ? {this.props.children} : null;
            </div>}
              );
            };

    export default playerInfo;

答案 4 :(得分:0)

除非先前的父状态发生更改并传递向下传播到功能组件的更新属性,否则功能组件不会重新呈现。您可以从有状态父级传递onClick处理程序,并在触发onClick时从无状态组件中调用它。父母将控制显示的切换并将其作为道具传递给孩子(参见下面的片段)。

要构建此功能,您应确定您的HOC(更高阶组件)是否应该负责UI状态。如果是这样,那么它可以确定其子组件是否应该处于打开状态,然后将其作为属性传递给子状态。如果这是一个应该独立于周围世界而打开和关闭的组件,那么它应该有自己的状态。例如,如果您正在创建一个选项卡式窗口小部件,它应该可以控制自己的打开和关闭状态。

&#13;
&#13;
class App extends React.Component {

  state= {
    isOpen: false
  }
  
  handleClick = () => {
    this.setState({
      isOpen: !this.state.isOpen
    })
  }
  
  render() {
    return (
      <div>
        <YourComponent isOpen={this.state.isOpen} handleClick={this.handleClick} />
      </div>
    )
  }
}


const YourComponent = ({isOpen, handleClick} = props) => {
  const onClick = () => {
    if (handleClick) {
      handleClick();
    }
  }
  
  return (
    <div onClick={onClick}>
      {isOpen ? 
        <h2>it is open</h2>
       :
        <h2>it is closed</h2>
      }
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>
&#13;
&#13;
&#13;

如果你主要关注的是将属性/方法传递给太多的组件,你可以创建一个pure component,它可以让你访问状态但不是React Component子类的所有开销。您还可以考虑使用像Redux这样的状态管理工具。

答案 5 :(得分:0)

从React的16.8版本开始,您可以使用钩子(在这种情况下为useState)来处理无状态组件。以最简单的形式,它可以这样实现:

import React, { useState } from 'react';

const PlayerInfo = (props) => {
  const [showPanel, togglePanel] = useState(false);

  return (
    <div onClick={() => togglePanel(!showPanel) }>Toggle</div>
   {showPanel && (
     <div className="info-panel">
        {props.children}
      </div>
   )}
   </div>
  );
};

export default PlayerInfo;