Реакция. js: как установить активный флаг в одном из нескольких идентичных потомков? - PullRequest
0 голосов
/ 10 марта 2020

У меня есть родительский компонент с несколькими одинаковыми дочерними элементами, из которых только один может быть активным одновременно. Активное состояние устанавливается внутренним событием самого ребенка, например, нажатием кнопки, а не родителем. Я хочу, чтобы активное состояние было сброшено вызовом одного из братьев и сестер, но я не могу найти способ, чтобы братья и сестры могли вызывать методы друг друга. Я пробовал ссылки, но они доступны только от родителя, и я не могу найти способ сделать дочернюю ссылку доступной внутри себя, не поддерживая список ссылок на родителя, который мне не нужен, так как мне нужно только сохранить текущую активную.

Простой пример

Например,

<Parent>
  <Child active={false}/>
  <Child active={false}/>
  <Child active={true}/>
</Parent>

, где дочерний элемент похож на

export class Child extends React.Component {
  constructor() {
    super(props);
    state  = {
     active: props.active; 
    }
  } 

  setActive(active) {
    setState ({active : active});
  }

  onclick = () => {
     // get currently active other sibling?
     // call setActive direct on other sibling.
     // e.g. other.setActive(false);

     // set current to active using the same method
     this.setActive(true);
  }
  render() {
    return (
      <button onclick={this.onclick}/>
      <p>current state={this.state.active ? "active": "inactive"}
    );
  }
}

Я попытался передать функции родительского setActiveRef и getActiveRef как просит детей поддерживать единый общий ref (активный) и использовать getActiveRef (). current.setActive напрямую, но я не могу найти способ получить доступ к ref дочернего компонента изнутри себя для отправки в setActiveRef () на родитель в первую очередь.

любой совет высоко ценится. спасибо

Ответы [ 3 ]

0 голосов
/ 10 марта 2020

Предположим, у вас есть массив дочерних компонентов, каждый из этих дочерних элементов будет иметь реквизит, называемый активным, поэтому вы можете использовать переменную состояния для хранения массива дочерних элементов, поэтому, когда один из дочерних элементов будет обновлен, и вызвать повторное рендеринг каждого из дочерних компонентов.

import React from "react";
const Parent = () => {
  const childs = [{ active: false }, { active: false }, { active: false }];

  const [childrens, setChildrens] = React.useState(childs);

  const onOptionSelected = idx => {
    setChildrens(prevOptions =>
      prevOptions.map((opt, id) => {
        opt.active = id === idx;
        return opt;
      })
    );
  };

  return (
    <div>
      {childrens.map((child, id) => {
        return (
          <Child
            key={id}
            id={id}
            active={child.active}
            onOptionSelected={onOptionSelected}
          />
        );
      })}
    </div>
  );
};

const Child = ({ id, active, onOptionSelected }) => {
  console.log(id)
  const onClick = () => {
    onOptionSelected(id);
  };

  return (
    <>
      <button onClick={onClick}>set active</button>
      <p>current state={active ? "active" : "inactive"}</p>
    </>
  );
};

export default Parent;
0 голосов
/ 10 марта 2020

Короче говоря, это не то, как устроен React - дети не смогут напрямую изменять состояние друг друга. Простая аналогия - буквальный родитель и их дети. Вы хотите, чтобы дети знали, когда пора поднять руку, но они не принимают указания друг от друга, только от мамы или папы. Что вы МОЖЕТЕ сделать, так это рассказать детям, как общаться со своими родителями, и позволить родителям справиться с этим.

Я уверен, что есть лучшие способы, но вот код:

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

        state = {
            activeChild: "Jimmy"
        }   
    };

    // Let the parents listen for requests  
    handleChildRequest (name) => {
        this.setState({activeChild: name});
    };

    render() {          
        <div>
            <Child active={this.state.activeChild === "Jimmy"} name="Jimmy" handleRequest={this.handleChildRequest} />
            <Child active={this.state.activeChild === "Sally"} name="Sally" handleRequest={this.handleChildRequest} />
            <Child active={this.state.activeChild === "Fred"} name="Fred" handleRequest={this.handleChildRequest} />
        </div>
    };  
}

export class Child extends React.Component {
  constructor() {
    super(props);
    // Lets forget about local state, we don't need it!
  } 

  onclick = () => {
     this.props.handleRequest(this.props.name); 
  }
  render() {
    return (
      <button onclick={this.onclick}/>
      <p>current state={this.props.active ? "active": "inactive"}
    );
  }
}
0 голосов
/ 10 марта 2020

Ответьте на мой собственный вопрос, используя компонент, передающий (this) ссылки на родительский обратный вызов. Это не полный код, но я иллюстрирую суть. Кажется, работает нормально для моего варианта использования (обновление местоположений карты в реальном времени), который является более сложным, чем этот упрощенный пример.

родительский компонент передает обратные вызовы дочерним элементам для сохранения ссылки на активный компонент



export class Parent extends React.Component {
  activeChild = undefined;

  setActiveChild = (child) => {
    activeChild = child;
  }

  getActiveChild = () => {
    return activeChild;
  }

  // set up some callback props on each child
  render() {
    return (
      <Parent>
        <Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
        <Child active={false} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
        <Child active={true} setActiveChild={setActiveChild} getActiveChild={getActiveChild}/>
      </Parent>
    )
}

каждый ребенок просто перезванивает родителю, используя обратные вызовы, и передает себя сам это позволяет устанавливать состояние внутри компонента, вызывая повторную визуализацию при изменении значений.

export class Child extends React.Component {
  onclick = () => {
     // get currently active other sibling stored in parent
     let active = this.props.getActiveChild();

     // call setActive directly on other sibling.
     active.setActive(false);

     // store currently active child in parent
     this.props.setActiveChild(this);

     // set 'this' component to active using the same method
     this.setActive(true);
  }}

Критика и улучшения приветствуются.

спасибо

...