【问题标题】:How to access and change child's state from Parent component如何从父组件访问和更改孩子的状态
【发布时间】:2020-09-03 15:19:54
【问题描述】:

您获得了无法控制的子组件。 父组件存储计数器的数量并呈现该数量的子组件。它有两个按钮添加计数器和递增所有计数器。

  1. 您不能编辑子组件。

  2. 实施 incrementCounters 使其递增所有子项的计数器 被渲染的组件。

    增量计数器 = () => { // TODO: 实现 };


export class ChildCounter extends React.Component{
    state = {
        counter: 0
    };

    increment = () => {
        this.setState(({ counter }) => ({
            counter: counter + 1
        }));
    };

    render() {
        return (
            <div>
                Counter: {this.state.counter}
                <button onClick={this.increment}>+</button>
            </div>
        );
    }
}

import {ChildCounter} from "./Child";


class App extends React.Component {

    state = {counters:1}


    addCounter = () => {
     let counterRn = this.state.counters + 1;
     this.setState({counters:counterRn})
    };

   incrementAll = () => {

   }

   render() {
   return (
       <div>
                { new Array(this.state.counters).fill(0).map((_, index) => {
                    return <ChildCounter key={index} />;
                })
                }
           <br/>
           <button style={{marginRight:10}} onClick={this.addCounter}>Add Counter</button>
           <button onClick={this.incrementAll}>Increment all counters</button>
       </div>
   )
   }
}

【问题讨论】:

  • 我不能改变子组件 为什么不呢?这将是迄今为止 IMO 最直接的方法
  • 应该这样解决

标签: javascript reactjs typescript react-native


【解决方案1】:

首先我不知道你为什么不想用状态来做,我认为这会好得多 通过使用一个计数器值数组,然后一次将它们全部递增,并将您的子组件作为控制器组件。

使用 refs,我无法想出一个好的解决方案(不确定)。 所以我在这里传递了代码,我所做的是,我使用了一个名为 useImperativeHandle 的反应钩子来让你的子组件获取 ref 并暴露内部的 increment 方法。 然后我在你的父组件中添加了一个引用数组。 因此,当用户单击全部增量时,基本上您会遍历您的 refs 并调用内部增量方法。 我仍然不确定实现引用数组的正确方法,但我认为应该这样做。 我已经用钩子写了这个,让它更容易理解。


import React, {
  useState,
  useEffect,
  createRef,
  forwardRef,
  useImperativeHandle
} from "react";

const ChildCounter = forwardRef((props, ref) => {
  const [counter, setCounter] = useState(0);

  const increment = () => {
    setCounter((c) => c + 1);
  };

  useImperativeHandle(ref, () => ({
    increment
  }));

  return (
    <div>
      Counter: {counter}
      <button onClick={increment}>+</button>
    </div>
  );
});

const App = () => {
  const [counters, setCounters] = useState(1);
  const [elRefs, setElRefs] = useState([]);

  const addCounter = () => {
    setCounters((c) => c + 1);
  };

  useEffect(() => {
    setElRefs((elRefs) =>
      Array(counters)
        .fill()
        .map((_, i) => elRefs[i] || createRef())
    );
  }, [counters]);

  const incrementAll = () => {
    for (let i = 0; i < elRefs.length; i++) {
      if (elRefs[i] && elRefs[i].current) {
        elRefs[i].current.increment();
      }
    }
  };

  return (
    <div>
      {new Array(counters).fill(0).map((_, index) => {
        return <ChildCounter ref={elRefs[index]} key={index} />;
      })}
      <br />
      <button style={{ marginRight: 10 }} onClick={addCounter}>
        Add Counter
      </button>
      <button onClick={incrementAll}>Increment all counters</button>
    </div>
  );
};

export default App;

https://codesandbox.io/s/trusting-neumann-37e2q?file=/src/App.js:0-1327

【讨论】:

  • 这需要改变孩子,但 OP 说这是不允许的。这肯定是 X/Y 问题,但仍然
  • 是的,这是我想到的唯一一个不会影响子组件,只会扩展一些属性的解决方案,但仍然需要添加更多的东西,子组件还需要一些工作来支持ref,在示例中它不支持
  • 问题是:给了你无法控制的子组件。父组件存储计数器的数量并呈现该数量的子组件。它有两个按钮添加计数器和增加所有计数器。 ? 不允许编辑子组件。 ? 实现 incrementCounters 以便它增加所有渲染子组件的计数器。
  • incrementCounters = () => { // TODO: 实现 };
  • 是的,我认为应该这样做。我还附上了一个代码框链接供您试用。
【解决方案2】:

技术上可能通过从父级传递一个引用而不更改子级来实现,并且让父级在@987654323 时使用引用访问子级的increment 方法@ 被调用:

childCounterChildren = [];

incrementAll = () => {
    for (const counter of this.childCounterChildren) {
        counter.increment();
    }
}
<ChildCounter
    key={index}
    ref={(childCounter) => { if (childCounter) { this.childCounterChildren.push(childCounter); }}}
/>;

class ChildCounter extends React.Component {
    state = {
        counter: 0
    };

    increment = () => {
        this.setState(({ counter }) => ({
            counter: counter + 1
        }));
    };

    render() {
        return (
            <div>
                Counter: {this.state.counter}
                <button onClick={this.increment}>+</button>
            </div>
        );
    }
}

class App extends React.Component {
    state = { counters: 1 }
    childCounterChildren = [];
    addCounter = () => {
        let counterRn = this.state.counters + 1;
        this.setState({ counters: counterRn })
    };

    incrementAll = () => {
        for (const counter of this.childCounterChildren) {
            counter.increment();
        }
    }
    componentWillUpdate() {
      this.childCounterChildren.length = 0;
    }

    render() {
        return (
            <div>
                {new Array(this.state.counters).fill(0).map((_, index) => {
                    return <ChildCounter
                        key={index}
                        ref={(childCounter) => { if (childCounter) { this.childCounterChildren.push(childCounter); }}}
                    />;
                })
                }
                <br />
                <button style={{ marginRight: 10 }} onClick={this.addCounter}>Add Counter</button>
                <button onClick={this.incrementAll}>Increment all counters</button>
            </div>
        )
    }
}

ReactDOM.render(<App />, document.querySelector('.react'));
<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 class='react'></div>

但这很奇怪。对于现实世界的问题,我建议改为lifting state up

【讨论】:

    猜你喜欢
    • 2016-12-21
    • 1970-01-01
    • 2018-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多