#reactjs
Вопрос:
Я пытаюсь создать панель мониторинга с помощью React. Существует несколько компонентов: приложение, блок и другие дочерние компоненты, назовем их Контентом.
Блок-это простая загрузочная карточка с названием, классами и некоторыми css. В приложении я вызываю блок и передаю компоненты содержимого. Но в некоторых компонентах контента есть функции, которые могут изменять компонент блока (например, добавлять или удалять классы).
Теперь я использую состояния в компонентах приложений и контента для изменения блока, но я не думаю, что это правильный подход.
Добавление setState
компонента в блок невозможно, как я знаю, потому что нет способа изменить реквизит.
Как я могу изменить состояния блоков внутри компонентов содержимого?
Пример:
function App() {
return (
<div>
<Block
id="users-component"
title="Users Table"
classes=[]
content={
<MyTable class="users" />
}
/>
<Block
id="status-component"
title="Status Component"
classes=[]
content={
<Status class="status" />
}
/>
<Block
id="bdays-component"
title="Bdays Component"
classes=[]
content={
<Bdays class="bdays" />
}
/>
</div>
)
}
function Block(props) {
return (
<div id={props.id} className={props.classes.join(" ")}>
<h2>{props.title}</h2>
{props.content}
</div>
)
}
function MyTable(props) {
return (
<table className={props.class}></table>
)
}
function Status(props) {
const handleClick = (title) => {
changeTitle(title) // changes title in Block
}
return (
<div className={props.class}>
<button onClick={() => handleClick("newTitle")}>New Title</button>
</div>
)
}
function Bdays(props) {
const handleClick = (class) => {
addNewClass(class) // add new class to array "classes" in it's block
}
return (
<div className={props.class}>
<button onClick={() => handleClick("newClass")}>New Class</button>
</div>
)
}
P.S. Извините за мой английский)
Ответ №1:
Проблема решается путем перемещения компонентов содержимого в тело блока и использования React.cloneElement. Теперь это выглядит так:
function App() {
return (
<div>
<Block
id="users-component"
title="Users Table"
>
<MyTable class="users" />
</Block>
<Block
id="status-component"
title="Status Component"
>
<Status class="status" />
</Block>
<Block
id="bdays-component"
title="Bdays Component"
>
<Bdays class="bdays" />
</Block>
</div>
)
}
function Block(props) {
[classes, addClasses] = useState([""])
[title, renewTitle] = useState(props.title)
return (
<div id={props.id} className={classes.join(" ")}>
<h2>{props.title}</h2>
{React.cloneElement(children, {addClasses, renewTitle})}
</div>
)
}
function MyTable(props) {
return (
<table> </table>
)
}
function Status(props) {
const handleClick = (title) => {
props.renewTitle(title) // changes title in Block
}
return (
<div>
<button onClick={() => handleClick("newTitle")}>New Title</button>
</div>
)
}
function Bdays(props) {
const handleClick = (class) => {
props.addClasses([class]) // add new class to array "classes" in it's block
}
return (
<div className={props.class}>
<button onClick={() => handleClick("newClass")}>New Class</button>
</div>
)
}
Ответ №2:
Делая это таким образом, вы не сможете делиться состоянием между этими компонентами, так как они являются братьями и сестрами (хотя я мог бы исправить это).
Вместо этого вы могли бы сделать так, myTable
чтобы быть дочерним элементом Block
, и передать функцию useState в myTable
качестве опоры, таким образом, вы можете изменить состояние Block
с myTable
Это пример реализации
import { useState } from "react";
export default function App() {
return (
<Block
id="users-component"
content={
<MyTable class="users" />
}
/>
);
}
function Block(props) {
const [headerText, setHeaderText] = useState("Marco!")
return (
<div id={props.id}>
<h1> {headerText} </h1>
<MyTable changeHeaderText = {setHeaderText} />
</div>
)
}
function MyTable(props) {
return (
<table className={props.class}>
<tbody>
<tr>
<td>
I'm a table and I can change my parent's header text!
</td>
</tr>
<tr>
<td>
<button
onClick = {() => props.changeHeaderText("polo!")} >Click me!</button>
</td>
</tr>
</tbody>
</table>
)
}
Комментарии:
1. Спасибо. Но у меня есть несколько различных дочерних компонентов, таких как MyTable, и я использую многократную копию блока в приложении с ними в качестве реквизита. Таким образом, невозможно перевести все из них в функцию «Блок». И я сейчас не понимаю, как люди используют компоненты в качестве шаблона, чтобы вставлять в них другие компоненты. Они создают это в каждом дочернем компоненте?
2. Не могли бы вы подробнее пояснить примерами, как вы планируете реализовать то, что говорите?
3. Я обновил пример и добавил комментарии. Я хочу использовать «changeTitle» и «addNewClass» в <Блоке/>, без состояний в <Блоке/><Приложении/>
4. Я не думаю, что есть способ сделать это, не помещая состояние в основной компонент приложения. Лучше всего использовать Redux или крючок useReducer для создания глобального состояния, которое может передаваться между всеми компонентами
5. Спасибо за ответы. Я думаю, я изменю структуру проекта, чтобы избежать этой проблемы.