score:1

Accepted answer

render all items, even if they are filtered out, and just hide the items filtered out using css (display: none):

const printlist = () => {
    //if tag in filter has been added        
    if (state.tags.length > 0) {
        // create a set of tags in state once
        const tagsset = new set(state.tags.map(item => item.id));
        return data.assets.map(elem => {
            //hide if no tag is found
            const hideelem = !elem.tags.some(item => tagsset.has(item.id));

            //if found, return asset 
            return <item key={elem.title} data={elem} hide={hideelem} />;
        })
    } else {
        return data.assets.map(elem => (<item key={elem.title} data={elem} /> ));
    }
};

and in the item itself, use the hide prop to hide the item with css using the style attribute or a css class:

return (
    <div classname="item" style={{ display: props.hide ? 'none' : 'block' }}>

you can also simplify printlist() a bit more, by always creating the set, even if state.tags is empty, and if it's empty hideelem would be false:

const printlist = () => {
  const tagsset = new set(state.tags.map(item => item.id));

  return data.assets.map(elem => {
    //hide if state.tags is empty or no selected tags
    const hideelem = tagsset.size > 0 && !elem.tags.some(item => tagsset.has(item.id));

    //if found, return asset 
    return (
      <item key={elem.title} data={elem} hide={hideelem} />
    );
  })
};

score:1

the problem that you are facing is that the cards that disappear are unmounted, meaning that their state is lost. the best solution is keeping the new custom tags you add to cards in the parent component, so it's persistent, no matter if the card is mounted or not. here are the modified files:

parent component

import react, {usestate} from 'react';
import item from './item'
import data from '../../data.json';

import './assets.scss'

function assets() {
    const [state, updatemethod] = usestate({tag: "", tags: []});

    const [childrentags, setchildrentags] = usestate(data.assets.map(elem => elem.tags));

    const addchildrentag = (index) => (tag) => {
        let newtags = array.from(childrentags)
        newtags[index] = [...newtags[index], tag]

        setchildrentags(newtags)
    }

    const removechildrentag = (index) => (i) => {
        let newtags = array.from(childrentags)
        newtags[index] = newtags[index].filter((elem, indx) => indx !== i)

        setchildrentags(newtags)
    }

    const printlist = () => {
        //if tag in filter has been added        
        if (state.tags.length > 0) {
            return data.assets.map((elem, index) => {
                //extract ids from obj into array
                const dataarr = elem.tags.map(item => item.id);
                const statearr = state.tags.map(item => item.id);

                //check if tag is found in asset
                const doestagexist = statearr.some(item => dataarr.includes(item));
                //if found, return asset 
                if (doestagexist) 
                    return (
                        <item 
                            key={elem.title} 
                            data={elem} 
                            customtags={childrentags[index]} 
                            addcustomtag={addchildrentag(index)}
                            removecustomtag={removechildrentag(index)}
                        />
                    )
            })
        } else {
            return data.assets.map((elem, index) => (
                <item 
                    key={elem.title} 
                    data={elem} 
                    customtags={childrentags[index]} 
                    addcustomtag={addchildrentag(index)}
                    removecustomtag={removechildrentag(index)}
                />
            ));
        }
    };

    const handleclick = () => {
        const newtag = {id: state.tag, text: state.tag};
        const copy = [...state.tags, newtag];

        if (state.tag !== "") updatemethod({tag: "", tags: copy});
    }

    const handlechange = e => updatemethod({tag: e.target.value, tags: state.tags});

    const handledelete = i => {
        const copy = [...state.tags];
        let removed = copy.filter((elem, indx) => indx !== i);

        updatemethod({tag: state.tag, tags: removed});
    }

    return (
        <div classname="assets">
            <div classname="asset__filter">
                <h3>add tags to filter</h3>
                <ul classname="asset__tag-list">
                    {state.tags.map((elem, i) => (
                        <li classname="asset__tag" key={`${elem.id}_${i}`} >
                            {elem.text}

                            <button classname="asset__tag-del" onclick={() => handledelete(i)}>x</button>
                        </li>
                    ))}
                </ul>

                <input 
                    type="text" 
                    value={state.tag}
                    onchange={handlechange} 
                    placeholder="enter new tag" 
                    classname="asset__tag-input"
                />

                <button classname="asset__btn" onclick={handleclick}>add</button>
            </div>

            <div classname="item__list-holder">
                {printlist()}
            </div>
        </div>
    );  
}

export default assets;

child component

import react, {usestate, useeffect} from 'react';

function item(props) {
    const [state, updatemethod] = usestate({tag: ""});
    cosnst tags = props.customtags
    cosnst addcustomtag = props.addcustomtag
    cosnst removecustomtag = props.removecustomtag

    const handleclick = () => {
        if (state.tag !== "") addcustomtag(state.tag);
    }

    const handlechange = e => updatemethod({tag: e.target.value});

    const handledelete = i => {
        removecustomtag(i);
    }

    const assets = props.data;

    return (
        <div classname="item">
            <img src={assets.url} alt="assets.title"/>
            <h1 classname="item__title">{assets.title}</h1>

            <div classname="item__tag-holder">
                <ul classname="item__tag-list">
                    {tags.map((elem, i) => (
                        <li classname="item__tag" key={`${elem.id}_${i}`} >
                            {elem.text}
                            <button classname="item__tag-del" onclick={() => handledelete(i)}>x</button>
                        </li>
                    ))}
                </ul>

                <input 
                    type="text" 
                    value={state.tag} 
                    onchange={handlechange} 
                    placeholder="enter new tag" 
                    classname="item__tag-input"
                />

                <button classname="item__btn" onclick={handleclick}>add</button>
            </div>
        </div>
    );
}

export default item;

hope this helps, i can add some comments if anything is unclear :)


Related Query

More Query from same tag