Как изменить состояние массива дочерних компонентов внутри родителя с помощью хуков? - PullRequest
1 голос
/ 20 июня 2020

У меня есть родительский компонент, у которого есть div, содержащий ol с пользовательскими компонентами внутри. Каждый из этих компонентов имеет состояние, в котором я сохраняю логическое значение «check». Мне нужна кнопка в моем родительском компоненте, которая очистит ВСЕ состояние дочерних компонентов и установит для него значение false. Я пытаюсь использовать useRef и useImperativeHandle, но не могу понять.

CountryListing (дочерний компонент)

export default function CountryListing({ country }, ref) {
const countryRef = useRef();
const [checkbox, toggleCheckbox] = useState(false)

const clearCheckbox = () => {
    toggleCheckbox(false);
}

 useImperativeHandle(ref, () => ({
     clear: () => {ref.current.clearCheckbox()}
    }
));

return (
    <li key={country}  >
        <div className="country-listing-container">
            <input type="checkbox" className="country-checkbox" id={country + "Checkbox"} ref={countryRef} value={checkbox} onChange={() => toggleCheckbox(!checkbox)}></input>
            <img src={'https://storage.googleapis.com/flags-bucket/flags/' + getImageFor(country)} className="country-flag" alt={country + "'s flag icon."}></img>
            <p className="country-name"> {country} </p>
        </div>
    </li>
);

}

Сворачиваемый (родительский компонент)

export default function Collapsible() {
  const [open, togglePanel] = useState(false);
  const [countries] = useState(["Mexico", "Brazil", "United Kingdom", "Not Provided - Canada", "United States", "Russia", "Australia", "Peru", "China", "Taiwan", "Pakistan", "Yemen", "Thailand", "New Zealand", "Czech Republic", "Spain", "Japan", "South Korea", "South Africa", "Argentina", "Afghanistan", "Angola", "Andorra", "Armenia", "Fiji", "Finland", "Estonia", "Ecuador", "Egypt", "Hungary", "Iran", "Ireland", "Austria", "Poland", "Kuwait", "Libya", "Monaco", "Mongolia", "Mozambique", "Nepal", "Italy", "Norway", "Barbados", "Bolivia", "Bulgaria", "Chile", "Colombia"]);

  const [searchTerm, setSearchTerm] = useState('');
  const [searchResults, setSearchResults] = useState(countries);

  //TODO title is not showing completely
  const [title] = "Locations";
  const ref = useRef(false);

  const countryComponents = (countries) => {
    const countriesList = countries.map(thisCountry => <CountryListing country={thisCountry} ref={ref} />);
    return countriesList;
  }

  const [countryComponentsList, modifyCountryComponentsList] = useState(countryComponents(searchResults))
  const clearAllAction = () => {
    ref.current.clearCheckbox();
  }


  const Initials = (countries) => {
    const [countryInitials] = useState(getInitials(countries.countries));
    const listInitials = countryInitials.map((initial) => <li key={initial}> {initial} </li>);

    return (
      <ul style={{ listStyleType: "none" }}>{listInitials}</ul>
    )
  }

  const search = (term) => {
    console.log('searchTerm is ' + searchTerm);
    if (!term) {
      setSearchResults(countries);
    }
    else {
      const results = countries.filter(country => country.toLowerCase().includes(term));
      setSearchResults(results);
    }
  }

  return (
    <>
      <div className="top-header">
        <input type="text" placeholder="Filter Locations" value={searchTerm}
          onKeyUp={(event) => {
            setSearchTerm(event.target.value);
            search(searchTerm);
          }}
          onChange={(event) => {
            setSearchTerm(event.target.value);
            search(searchTerm);
          }}
          onLoad={(event) => {
            setSearchTerm(event.target.value);
            search(searchTerm);
          }}
        >
        </input>
        <button onClick={() => search(searchTerm)}> S </button>
      </div>
      <div>
        <div onClick={() => togglePanel(!open)} className="header">
          {title}
        </div>
        {
          open ? (
            <>
              <div className="before-content"><button id="clearAllButton" onClick={clearAllAction()} > X Clear All </button></div>
              <div className="content">
                <div className="basic-grid">
                  <div className="card">

                    <ol style={{ listStyleType: "none" }}>
                      {countryComponentsList}
                    </ol>
                  </div>

                  <div className="card">
                    <Initials countries={countries} />
                  </div>
                </div>
              </div>
            </>
          ) : null
        }
      </div >
    </>
  );
}


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

Ответы [ 2 ]

1 голос
/ 20 июня 2020

На самом деле я не очень хорошо знаком с тем, как вы пытаетесь это сделать, но я могу предложить альтернативу. Вместо этого вы можете сделать что-то вроде этого:

const [checkedElements, setCheckedElements] = useState([]);

Затем, когда вы сопоставляете и создаете элементы, вы можете использовать индекс метода карты, чтобы сказать что-то вроде

onClick={()=>setCheckedElements([...checkedElements, index])}
checked={checkedElements.includes(index)}

Вы можете очистить все элементы, нажав на кнопку, добавив это на кнопку:

onClick={setCheckedElements([])}
0 голосов
/ 20 июня 2020

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

const Parent = () => {
  const [todos, setTodos] = React.useState([
    { id: 1, label: "Wake up", completed: true },
    { id: 2, label: "Make Coffee", completed: false },
    { id: 3, label: "Go to Work", completed: false }
  ]);
  const checkAll = React.useMemo(() => {
    const filterTodos = Object.assign([], todos);
    return (
      filterTodos.filter(f => f.completed === true).length === todos.length
    );
  }, [todos]);
  const checkAllTodos = e => {
    const value = e.target.checked;
    setTodos(current => current.map(f => ({ ...f, completed: value })));
  };
  const checkTodo = React.useCallback(id => {
    setTodos(current =>
      current.map(item => {
        if (item.id === id) return { ...item, completed: !item.completed };
        else return item;
      })
    );
  }, []);
  return (
    <div>
      <ul>
        <li>
          <input type="checkbox" checked={checkAll} onChange={checkAllTodos} />
          todos {checkAll}
        </li>
        {todos.map(item => (
          <Todo
            id={item.id}
            checked={item.completed}
            key={item.id}
            change={checkTodo}
          >
            {item.label}
          </Todo>
        ))}
      </ul>
    </div>
  );
};
const Todo = React.memo(({ checked, children, id, change }) => (
  <li>
    <input type="checkbox" checked={checked} onChange={() => change(id)} />
    {children}
  </li>
));
ReactDOM.render(<Parent/>,document.getElementById("root"))
<script src="https://unpkg.com/react@16/umd/react.production.min.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js" crossorigin></script>
<div id="root"></div>
...