【问题标题】:ReactJS: Dynamically add a component on clickReactJS:点击时动态添加组件
【发布时间】:2019-09-20 01:53:23
【问题描述】:

我有一个菜单按钮,按下时必须添加一个新组件。它似乎有效(如果我手动调用该函数来添加它们显示的组件)。问题是,如果我单击按钮,它们不会显示出来,我想是因为我应该使用 setState 来重绘它们。我不确定如何在另一个函数/组件中调用另一个组件的 setState。

这是我的 index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Menu from './Menu';
import * as serviceWorker from './serviceWorker';
import Blocks from './Block.js';


ReactDOM.render(
    <div className="Main-container">
        <Menu />
        <Blocks />
    </div>
    , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers:
serviceWorker.unregister();

然后我有 Menu.js

import React from 'react';
import './Menu.css';
import {blocksHandler} from './Block.js';

class Menu extends React.Component {

  constructor(props) {

    super(props);
    this.state = {value: ''};

    this.handleAdd = this.handleAdd.bind(this);

  }

  handleAdd(event) {
    blocksHandler.add('lol');
    console.log(blocksHandler.render());
  }

  render() {
    return (
      <div className="Menu">
        <header className="Menu-header">
          <button className="Menu-button" onClick={this.handleAdd}>Add block</button>
        </header>
      </div>
    );
  }
}

export default Menu;

最后是 Block.js

import React from 'react';
import './Block.css';

// this function adds components to an array and returns them

let blocksHandler = (function() {
    let blocks = [];
    return {
        add: function(block) {
            blocks.push(block);
        },
        render: function() {
            return blocks;
        }
    }
})();

class Block extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            title: '',
            content: ''
        };

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

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

    handleSubmit(event) {
        alert('A name was submitted: ' + this.state.title);
        event.preventDefault();
    }

    render() {
      return (
        <div className="Block-container">
            <form onSubmit={this.handleSubmit}>
            <div className="Block-title">
                <label>
                    Block title:
                    <input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
                </label>
            </div>
            <div className="Block-content">
                <label>
                    Block content:
                    <input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
                </label>
            </div>
            <input type="submit" value="Save" />
            </form>
        </div>
      );
    }
}

class Blocks extends React.Component {

    render() {
        return (
            <div>
                {blocksHandler.render().map(i => (
                    <Block key={i} />
                ))}
            </div>
        )
    }
}


export default Blocks;
export {blocksHandler};

我是 React 的初学者,所以我什至不确定我的方法是否正确。感谢您提供的任何帮助。

【问题讨论】:

  • 您可以使用道具将父状态传递给子状态。如果将 props 传递给 children 包含很多子组件,您也可以使用 context 传递 props。 reactjs.org/docs/context.html 最好的解决方案是根本不使用 React 状态,而使用更健壮的状态管理系统,Redux 是为了解决这个问题。
  • 感谢您的帖子。我正在尝试学习 ReactJS,而现在添加 Redux 可能会使事情变得过于复杂。我会看看你的链接。
  • 是的,如果你刚刚学习,使用 Redux 可能会让事情变得混乱,.. 我可能会给你一个非常简单的 sn-p 可能会有所帮助。

标签: javascript reactjs components dynamically-generated


【解决方案1】:

下面我敲了一个非常简单的父/子类型设置,..

Parent 负责渲染 Button,这里我只是使用了一个简单的编号数组。当您单击任何按钮时,它会调用 Parent 中的 setState,这反过来会导致 Parent 重新渲染它的 Children。

注意:我也使用过 React Hooks 来做这件事,我只是找到了更多 自然且更易于使用。可以使用Classes,原理相同 适用。

const {useState} = React;

function Child(props) {
  const {caption} = props;
  const {lines, setLines} = props.pstate;
  return <button onClick={() => {
    setLines([...lines, lines.length]);
  }}>
    {caption}
  </button>;
}

function Parent(props) {
  const [lines, setLines] = useState([0]);  
  return lines.map(m => <Child key={m} caption={`Click ${m}`} pstate={{lines, setLines}}/>);
}


ReactDOM.render(<React.Fragment>
  <Parent/>
</React.Fragment>, document.querySelector('#mount'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="mount"></div>

【讨论】:

  • @devamat 欢迎您,如果上面的任何内容看起来令人困惑,请离开。如果你想对上面的 sn-p 做一点练习,看看你是否可以改变它,以便每次只启用最后一个按钮。
【解决方案2】:

您可以将它放在 Menu.js 中,而不是将 blocksHandlers 创建为单独的函数,如下所示 *

class Block extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            title: '',
            content: ''

        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }


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

    handleSubmit(event) {
        alert('A name was submitted: ' + this.state.title);

   event.preventDefault();
    }

    render() {
      return (
        <div className="Block-container">
            <form onSubmit={this.handleSubmit}>
            <div className="Block-title">
                <label>
                    Block title:
                    <input type="text" name="title" value={this.state.value} onChange={this.handleChange} />
                </label>
            </div>
            <div className="Block-content">
                <label>
                    Block content:
                    <input type="text" name="content" value={this.state.value} onChange={this.handleChange} />
                </label>
            </div>
            <input type="submit" value="Save" />
            </form>
        </div>
      );
    }
}

Menu.js

class Menu extends React.Component {

  constructor(props) {

    super(props);
    this.state = {value: '',blocksArray:[]};

    this.handleAdd = this.handleAdd.bind(this);

  }

  handleAdd() {
   this.setState({
        blocksArray:this.state.blocksArray.push(block)
     })

  }

renderBlocks = ()=>{
      this.state.blocksArray.map(block=> <Block/>)
 }
  render() {
    return (
      <div className="Menu">
        <header className="Menu-header">
          <button className="Menu-button" onClick={()=>this.handleAdd()}>Add block</button>
        </header>
    {this.renderBlocks()}

      </div>
    );
  }
}

export default Menu;

【讨论】:

  • 感谢您的帖子。我无法使这段代码工作。我在 blocksArray: this.state.blocksArray.push(block) 收到错误,我将其更改为 blocksArray: this.state.blocksArray.push('lol') 但没有运气,我仍然收到错误:TypeError: this .state.blocksArray.map 不是一个函数。可能由于某种原因数组为空。
  • 其实在handleAdd()里面,你需要给blocks的配置,比如this.state.blocksArray.push(/*give the data required to render Block component*/)
猜你喜欢
  • 1970-01-01
  • 2021-06-28
  • 1970-01-01
  • 1970-01-01
  • 2017-12-02
  • 1970-01-01
  • 2018-09-17
  • 1970-01-01
  • 2017-01-18
相关资源
最近更新 更多