0

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)))

3 Answers 3

1

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')
);
Sign up to request clarification or add additional context in comments.

Comments

1

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.

1 Comment

@Слава Иванов. This is illustrated in Lifting State Up
0

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'));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.