I have the following:
const OrderItems = (props) => {
const { Order } = props.res
return (
<div>
{Order.map(order => {
if (order.OrderLines.OrderLine instanceof Array) {
order.OrderLines.OrderLine.map(line => (
<OrderLineItem data={line} />
))
}
})}
</div>
)
}
export default OrderItems
And the component:
const OrderLineItem = ({data}) => {
console.log(data)
return (
<div>Order Line Item</div>
)
}
Nothing is rendered and nothing is logged to the console.
However if I do:
const OrderItems = (props) => {
const { Order } = props.res
return (
<div>
{Order.map(order => {
if (order.OrderLines.OrderLine instanceof Array) {
order.OrderLines.OrderLine.map(line => console.log(line))
}
})}
</div>
)
}
The data I want is logged to the console. How can I render or pass the data from line please?
You don't return from .map() that's the reason. You can use && instead of if.
Try as the following instead:
{Order.map(order =>
order.OrderLines.OrderLine instanceof Array &&
order.OrderLines.OrderLine.map(line => <OrderLineItem data={line} />)
)}
While getting the props in the child component
const OrderLineItem = ({ data }) => {
console.log(data)
// DO WHAT YOU WANT TO DO WITH THE DATA!
return (
<div>Order Line Item</div>
)
}
Related
I want to call function when state data changes but not first loading.
This is my code.
const Page = (props) => {
const { data } = props;
const arrowDirection = (item) => {
if (item.arrow === 1) {
return "up";
} else {
return "down";
}
};
return (
<div>
{data &&
data.map((item, index) => (
<div key={index} className={arrowDirection(item)}>
{item.name}
</div>
))}
</div>
);
};
export default Page;
In here, props data changes automatically every few seconds.
So I want to change classname to up or down according to the status.
But when page loads, I don't want to call arrowDirection function so that the classname to be set as empty.
Eventually, I don't want to set classname for the first loaded data, but for the data from second changes.
How to do this?
I would try to update data in props and for first render let's say have item.arrow === null case for empty class. But if it is not possible, you may use useEffect+useRef hooks:
const Page = (props) => {
const { data } = props;
const d = useRef()
useEffect(() => {
d.current = true
}, []);
const arrowDirection = (item) => {
if (item.arrow === 1) {
return "up";
} else {
return "down";
}
};
return (
<div>
{data &&
data.map((item, index) => (
<div key={index} className={d.current ? arrowDirection(item) : ""}>
{item.name}
</div>
))}
</div>
);
};
I have three componente
ComponentFather ( grand Parent component )
ComponentChild ( Child component of ComponentFather)
ComponentChildChild ( Child component of ComponentChildChild)
The three components are connected so that each component is this child above.
I props data from ComponentFather to ComponentChildChild. And that work. But when I try to pring all data no work. Print only first data Chek code ->
export const ComponentFather = () => {
// this is only mockup data for example
let data = [
{
id: 2000004,
name: 'test123.pdf',
},
{
id: 2000005,
name: 'test123123.pdf',
},
];
return (
<ComponentChild data={data}>
</ComponentChild>
);
};
export const ComponentChild = ({data}) => {
// console.log(data); i got all data.
return (
{data.map((singleData) => {
<ComponentChildChild data={data}>
</ComponentChildChild>
})}
);
};
export const ComponentChildChild = ({data}) => {
return (
<div> { data.name } </div>
<div> { data.id } </div>
);
};
Code above no work......
Why?
What i am try also to set data.map inside ComponentChildChild component
export const ComponentChildChild = ({data}) => {
return (
{ data.map((singleData) => {
<div> { data.name } </div>
<div> { data.id } </div>
})}
);
};
Also no work. Work only when I hardcode example data[0].name
export const ComponentChildChild = ({data}) => {
return (
<div> { data[0].name } </div>
<div> { data[0].id } </div>
);
};
But sometimes i will have 10 items... I need to print each
pass singleData instead data
export const ComponentChild = ({data}) => {
return (
{data.map((singleData) => {
<ComponentChildChild data={singleData}>
</ComponentChildChild>
})}
);
};
I am trying to apply a change to only one item in an array depending on a prop.
I have this list:
interface FieldProps {
fileLimitWarning?: // I STILL NEED TO FIGURE THIS OUT
}
const DataField = (props: FieldProps) => {
const { fileLimitWarning, handleFileSelection } = props;
return (
<>
{fileLimitWarning && <p>This file exceeds the max size of 250mb</p>}
<input onChange={e => handleFileSelection(e.target.files)} />
</>
}
const ContainerOfDataField = () => {
const [fileLimitWarning, setFileLimitWarning] = useState(false);
const handleFileSelection = (files) => {
setFileLimitWarning(false);
const [currentFile] = files;
if(currentFile?.size <= 250000000) {
setFileLimitWarning(true);
}
}
return (
<ul>
{
fields?.map((promoField: Field, index: number) => {
return (
<DataField
key={index}
fileLimitWarning={fileLimitWarning}
handleFileSelection={handleFileSelection}
/>
);
})
}
</ul>
)
}
In this case what I want is to only return null in the DataField component when it corresponds. Right now whenever that fileLimitWarning === true on the ContainerOfDataField component, it hides/removes/deletes all of the Typography nodes from the DataField component. So what I need is to hide only the index that matches where the problem is coming from.
Is it clear?
I think ideally you would define fileLimitWarning in each iteration of your map, since (I assume) it is a property of the current item, rather than a global property:
return (
<ul>
{
fields?.map((promoField: Field, index: number) => {
// { currentFile } = promoField???
return (
<DataField
key={index}
fileLimitWarning={currentFile?.size <= 250000000}
handleFileSelection={handleFileSelection}
/>
);
})
}
</ul>
)
}
so the problem is I have a search functionality everything works, except that when an item has not been found, you see it should display the text "champion has not been found" but it is not. I would appreciate the help Where am I making a mistake?
import data from './data.json'
import './Champions.css'
import Skills from './Skills'
import CloseIcon from '#material-ui/icons/Close';
const Champions = ({searchValue}) => {
const [toggleShow, setToggleShow] = useState(false);
const [currentSelectedChampion, setCurrentSelectedChampion] = useState({});
const handleSelectChampion = (id) => {
if (!toggleShow) setToggleShow(true);
const currentChampion = data.filter((champ) => champ.id === id)[0];
setCurrentSelectedChampion(currentChampion);
};
function filterChampions(champion) {
return champion.name.toLowerCase().includes(searchValue.toLowerCase());
}
{data.filter(filterChampions).length === 0 && (<div className='not__found'>
<h1>No champion has been found</h1>
</div>)}
return (
<div className="champions">
{data.filter(filterChampions).map((champion) => {
return (
<div key={champion.id} onClick={() => handleSelectChampion(champion.id) } >
<div className="champion">
<img className="champion__Image" src={champion.image}></img>
<h4 className="champion__Name">{champion.name}</h4>
{toggleShow && currentSelectedChampion.id === champion.id && (
<>
<Skills currentChampion={currentSelectedChampion} />
<CloseIcon onClick={() => setToggleShow(false)}/>
</>
)}
</div>
</div>
);
})}
</div>
);
};
export default Champions
The map in line {data.filter(filterChampions).map((champion) => { will not return anything for empty array.
Consider the following examples.
[].map(e => 'called'); // []
[3].map(e => 'called'); // ['called']
So if {data.filter(filterChampions) returns an empty array the map will return empty array and not the div with class not__found.
What you need to do is something as following
const showChamtions = () => {
// Put the filtered data in a variable
const selectedChampions = champions.filter((element) => element.score > 12);
// If data is there do what you intend to do with it else not_found div
if (selectedChampions && selectedChampions.length > 0) {
return selectedChampions.map((element) => <p>{element.name}</p>);
} else {
return (
<div className="not__found">
<h1>No champion has been found</h1>
</div>
);
}
};
Example - https://codesandbox.io/s/map-on-empty-array-i6m1l?file=/src/App.js:349-741
You can modify your code similar to this using a conditional operator as well.
{data.filter(filterChampions).map((champion) => {
if(data.filter || champion){
return (
<div className='not__found'>
<h1>No champion has been found</h1>
</div>
)
}
This if statement is not nesserasy, if an item has not been found => data.filter(filterChampions) will be an empty array, the map function will return nothing, the if statement doesn't even run.
It you want to display the message, you could simply use this:
{data.filter(filterChampions).length === 0 && (<div className='not__found'>
<h1>No champion has been found</h1>
</div>)}
Just wondering the best way to pass the letterSelected into the useLazyQuery fetchMovies query, so that I don't have to use the static variable of "A". I was hoping there was a way to pass it directly into fetchMovies. useLazyQuery is an apollo query.
const BrowseMovies = () => {
const [fetchMovies, { data, loading}] = useLazyQuery(BROWSE_MOVIES_BY_LETTER, {
variables: {
firstLetter: "A"
}
})
return (
<div className="browserWrapper">
<h2>Browse Movies</h2>
<AlphabetSelect
pushLetterToParent={fetchMovies}
/>
{
data && !loading &&
data.browseMovies.map((movie: any) => {
return (
<div className="browseRow">
<a className="movieTitle">
{movie.name}
</a>
</div>
)
})
}
</div>
)
}
export default BrowseMovies
const AlphabetSelect = ({pushLetterToParent}: any) => {
const letters = ['A','B','C','D','E','F','G','H','I','J','K','L', 'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','#']
const [selectedLetter, setSelectedLetter] = useState("A")
const onLetterSelect = (letter: string) => {
setSelectedLetter(letter.toUpperCase())
pushLetterToParent(letter.toUpperCase())
}
return (
<div className="alphabetSelect">
{
letters.map((letter: string) => {
return(
<div
className={selectedLetter === letter ? 'letterSelectActive' : 'letterSelect'}
onClick={() => onLetterSelect(letter)}
>
{letter}
</div>
)
})
}
</div>
)
}
export default AlphabetSelect
This appears to be a problem solved by Lifting State Up. useLazyQuery takes a gql query and options and returns a function to execute the query at a later time. Sounds like you want the child component to update the variables config parameter.
BrowseMovies
Move firstLetter state BrowseMovies component
Update query parameters/options/config from state
Add useEffect to trigger fetch when state updates
Pass firstLetter state and setFirstLetter state updater to child component
const BrowseMovies = () => {
const [firstLetter, setFirstLetter] = useState('');
const [fetchMovies, { data, loading}] = useLazyQuery(
BROWSE_MOVIES_BY_LETTER,
{ variables: { firstLetter } } // <-- pass firstLetter state
);
useEffect(() => {
if (firstLetter) {
fetchMovies(); // <-- invoke fetch on state update
}
}, [firstLetter]);
return (
<div className="browserWrapper">
<h2>Browse Movies</h2>
<AlphabetSelect
pushLetterToParent={setFirstLetter} // <-- pass state updater
selectedLetter={firstLetter} // <-- pass state
/>
{
data && !loading &&
data.browseMovies.map((movie: any, index: number) => {
return (
<div key={index} className="browseRow">
<a className="movieTitle">
{movie.name}
</a>
</div>
)
})
}
</div>
)
}
AlphabetSelect
Attach pushLetterToParent callback to div's onClick handler
const AlphabetSelect = ({ pushLetterToParent, selectedLetter }: any) => {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#';
return (
<div className="alphabetSelect">
{
letters.split('').map((letter: string) => {
return(
<div
key={letter}
className={selectedLetter === letter ? 'letterSelectActive' : 'letterSelect'}
onClick={() => pushLetterToParent(letter.toUpperCase())}
>
{letter}
</div>
)
})
}
</div>
)
}