Estou trabalhando neste desafio do Frontend Mentor e já dominei a maioria das funcionalidades. Consigo filtrar os dados por suas isActive
propriedades para exibir os itens corretos, independentemente de estarem ativos ou não. O que não consigo descobrir é como atualizar isActive
o status de um item individual e fazer com que ele renderize o aplicativo novamente ao alternar para mover o item para o filtro correto.
Aqui está o código para App.jsx
:
function App() {
const [data, setData] = useState([]);
const [filteredData, setFilteredData] = useState(data);
const [activeFilter, setActiveFilter] = useState("All");
// Fetch and set the data.
useEffect(() => {
fetch("../data.json")
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error("error", error));
}, []);
// Filter the data
useEffect(() => {
if (activeFilter === "All") {
setFilteredData(data);
} else if (activeFilter === "Active") {
setFilteredData(data.filter((item) => item.isActive === true));
} else {
setFilteredData(data.filter((item) => item.isActive === false));
}
}, [activeFilter, data]);
return (
<div className="w-full h-full bg-linear-to-b from-[#040918] to-[#091540] py-6 px-3 flex flex-col text-white">
{/* Header */}
<Header />
{/* Options */}
<Options activeFilter={activeFilter} setActiveFilter={setActiveFilter} />
{/* Cards */}
{filteredData &&
filteredData.map((item) => (
<Card
logo={item.logo}
name={item.name}
description={item.description}
isActive={item.isActive}
key={item.name}
setFilteredData={setFilteredData}
filteredData={filteredData}
/>
))}
</div>
);
}
e aqui está Card.jsx
com a chave seletora:
const Card = ({
logo,
name,
description,
isActive,
filteredData,
setFilteredData,
}) => {
const [activeState, setActiveState] = useState(isActive);
function handleClick() {
setActiveState(!activeState);
}
return (
<div className="bg-neutral-700 p-4 rounded-xl border border-neutral-600 mt-4">
<div className="flex items-start gap-4">
{/* Logo */}
<img src={logo} alt="Extension Image" />
{/* Name and Description */}
<div className="flex flex-col gap-2 mb-8">
<h2 className="font-semibold text-xl">{name}</h2>
<p className="text-sm font-light">{description}</p>
</div>
</div>
{/* Remove Button */}
<div className="flex justify-between items-center">
<div className="border border-neutral-600 rounded-full px-3 py-1 flex items-center justify-center">
<button>Remove</button>
</div>
{/* Is Active Toggle */}
<div className="flex items-center justify-center">
<label
htmlFor={`${name}Toggle`}
className="flex items-center cursor-pointer "
>
<div className="relative">
<input
id={`${name}Toggle`}
type="checkbox"
className="sr-only"
onClick={handleClick}
/>
<div
className={`flex items-center ${
activeState ? "bg-red-400 " : "bg-gray-600 "
} w-11 h-6 rounded-full transition-all px-[2px]`}
>
<div
className={`bg-white w-5 h-5 rounded-full ${
activeState ? "translate-x-5" : "translate-x-0"
} transition-all`}
></div>
</div>
</div>
</label>
</div>
</div>
</div>
);
};
Eu estava tentando descobrir uma maneira dentro da handleclick
função do Card que correspondesse ao nome do card específico com o nome do item filteredData
e... inverter o status dele isActive
? Não consegui descobrir como fazer isso funcionar e nem tenho certeza se essa é a melhor maneira de fazer.
Parece que o estado está sendo duplicado. O componente pai está mantendo o estado da lista de itens e do filtro, e passando o
isActive
valor do estado para cada componente filho:Entretanto, o componente filho então duplica esse valor no estado que ele gerencia internamente:
Atualizar esse estado não tem efeito na lista no componente pai.
Em vez de rastrear o estado duplicado no componente filho, passe uma função de retorno de chamada para o componente filho que atualizaria o estado no componente pai. Por exemplo, suponha que o componente pai tenha isto:
(Observação: isso é, obviamente, feito à mão livre e não testado com seus dados. Mas o objetivo geral do acima é atualizar o estado para uma nova matriz de itens em que o
isActive
sinalizador é alternado para o item que corresponde ao especificado.)Em seguida, passe isso para o componente filho para atualizar esse estado:
Então o componente filho pode removê-lo
useState
completamente e invocar a função de retorno de chamada que foi passada a ele:Dessa forma, o estado pai é atualizado e os componentes pai e filho relevantes são renderizados novamente com o novo estado.
Problemas
Você implementou pelo menos alguns antipadrões do React:
filteredData
é derivado de "estado", por exemplo, derivado das fontesdata
eactiveFilter
do estado de verdade, e, como tal,filteredData
não deve ser também um estado do React. Você deve calcular o valor filtrado ao renderizar ou memorizar seu valor. Lembre-se de que implementar um acoplamentouseState
-useEffect
também é um antipadrão; em quase 100% dos casos de uso, esse acoplamento deve ser substituído pelouseMemo
hook.Card
componente não atualiza corretamente adata
fonte da verdade no componente pai. Ele recebeu umaisActive
prop, mas também deveria receber um retorno de chamada que trate da atualização do estado no componente pai.Sugestões de soluções
data
estado para calcular o valor dos dados filtrados derivados.data
estado e passe isso adiante,Card
em vez dofilteredData
estado e do atualizador de estado ( que será removido ).Exemplo: