简体   繁体   中英

Transferring data between React elements

Trying to figure out how to transfer data between siblings components. The idea is this: you need to make sure that only one child component has an "active" class (Only one div could be selected). Here is the code: https://codepen.io/slava4ka/pen/rNBoJGp

import React, {useState, useEffect} from 'react';
import styleFromCss from './Garbage.module.css';

const ChildComponent = (props) => {
    const [style, setStyle] = useState(`${styleFromCss.childComponent}`);
    const [active, setActive] = useState(false)
const setActiveStyle = () => {
    console.log("setActiveStyle");
    if (!active) {
        setStyle(`${styleFromCss.childComponent} ${styleFromCss.active}`)
        setActive(true)
    } else {
        setStyle(`${styleFromCss.childComponent}`);
        setActive(false)
    }
};

//console.log(props);
console.log(`id ${props.id} style ${style}`);
return (
    <div className={style} onClick={() => {
        props.updateData(props.id, () => setActiveStyle())
    }}>
        <h3>{props.name}</h3>
    </div>
   )
 };

const ParentComponent = (props) => {

const state = [{'id': 0, 'name': 'один'}, {'id': 1, 'name': 'два'}, {'id': 2, 'name': 'три'}];

const [clicked, setClicked] = useState(null);

const highlight = (id, makeChildActive) => {
    //console.log("click! " + id);
    setClicked(id);
    makeChildActive();
};

return (
    <div>
        {state.map(entity => <ChildComponent updateData={highlight}
                                             key={entity.id}
                                             id={entity.id}
                                             name={entity.name}
                                             isActive={(entity.id === 
     clicked) ? styleFromCss.active : null}
        />)}
    </div>
   )
};

export default ParentComponent;

styleFromCss.module.css:

.childComponent{
   width: 200px;
   height: 200px;
   border: 1px solid black;
   margin: 10px;
   float: left;
   text-align: center;
 }

.active{
    background-color: blueviolet;
 }

I tried to implement this through hooks, not classes. As a result, when you click on the selected component, its classes change, and nothing happens on its siblings. As I understand it, they simply do not redraw. The question is how to fix it? And is such an approach correct for the realization of a given goal? Or my idea is fundamentally wrong, I will be grateful for the advice)))

You need to pass a function down into the child from the parent that handles state change in the parent component for a piece of state that determines which id is "active", lets call that activeId. Then pass activeId into the child as a prop. In the child, compare the id to the activeId and apply the class accordingly.

You cannot use the Child's onClick method to set the style as when one Child is clicked, the other Children don't know that:((

Instead, when you click on one Child, it tells the Parent it is clicked (you do this correctly already with setClicked()), then the Parent can tell each Child whether they are active or not (by passing IsActive boolean), and each Child uses its props.isActive boolean to set its style:D

const ChildComponent = (props) => {

    let style = 'childComponent'
    if (props.isActive) style = style + ' active'

    return (
        <div className={style} onClick={() => {
            props.updateData(props.id)
        }}>
            <h3>{props.name}</h3>
        </div>
    )
};


const ParentComponent = (props) => {
    const state = [{'id': 0, 'name': 'один'}, {'id': 1, 'name': 'два'}, {'id': 2, 'name': 'три'}];

    const [clicked, setClicked] = React.useState(null);

    const highlight = (id) => {
        setClicked(id);
    };

    return (
        <div>
            {state.map(entity =>
                <ChildComponent updateData={highlight}
                                key={entity.id}
                                id={entity.id}
                                name={entity.name}
                                isActive={entity.id === clicked}
                />
            )}
        </div>
    )
};


ReactDOM.render(
    <ParentComponent/>,
    document.getElementById('root')
);

Correct, this is the wrong way to be thinking about it. If you only want one child to be selected at a time, you should keep the "which child is selected" data in the parent. Each child should not also manage its own version of its highlighted state; instead it should be a prop given to each child (each child is a pure function.) Try this:

const ChildComponent = props => {
  return (
    <div
      className={'childComponent' + (props.isActive ? ' active' : '')}
      onClick={() => props.setHighlighted(props.id)}
    >
      <h3>{props.name}</h3>
    </div>
  );
};

const ParentComponent = props => {
  const state = [
    { id: 0, name: 'один' },
    { id: 1, name: 'два' },
    { id: 2, name: 'три' }
  ];

  const [clicked, setClicked] = React.useState(null);

  return (
    <div>
      {state.map(entity => (
        <ChildComponent
          setHighlighted={setClicked}
          key={entity.id}
          id={entity.id}
          name={entity.name}
          isActive={entity.id === clicked ? 'active' : null}
        />
      ))}
    </div>
  );
};

ReactDOM.render(<ParentComponent />, document.getElementById('root'));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM