React : Rendered more hooks than during the previous render - javascript

I have a component that looks like this
function FirstCollapse(data, basis, comparisonMode, heading, referenceData, locations, expandAll) {
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
const innerHeadings = Object.keys(referenceData[heading]);
useEffect(() => {
setOpen(expandAll);
}, [expandAll]);
return(
<React.Fragment>
<Collapse in={open} timeout="auto" unmountOnExit>
{
(heading !== "NPU Resources") ?
innerHeadings.map((innerHeading, index) => {
return(
<>
{InnerCollapse(data, basis, comparisonMode, heading, innerHeading, referenceData, locations, innerHeadings, index, expandAll)}
</>);
})
: NPUResourceTable(data, basis, comparisonMode, locations, expandAll)
}
</Collapse>
</React.Fragment>
);
}
The NPUResourceTable function looks something like this:
export default function NPUResourceTable(data, basis, comparisonMode, locations, expandAll) {
try {
const innerHeadings = getAllHWResources(data, locations);
return(
<>
{
innerHeadings.map((innerHeading, index) => {
return(
<>
{InnerCollapse(data, basis, comparisonMode, locations, innerHeading, index, innerHeadings, expandAll)}
</>
);
})
}
</>
);
}
catch(error) {
console.log(error);
return(<InfoAlert contentText={dataErrorText} />);
}
}
The getAllHwResources function is as follows
function getAllHWResources(data, locations) {
const allResources = new Set();
data.map(snapShot => {
[...locations].map(loc => {
allResources.add(dataPoint[someKey]);
})
})
return [...allResources];
}
The data variable is an array of objects. The object might have variable keys and innerHeadings is basically the union of all unique keys and might change as we add new objects to the data variable.
What to do in this case
I tried resolving it through various ways but failed.

Related

canĀ“t access specific element of my array React

I've a problem with my react app.The navbar worked correctly before the ItemDetailContainer, and i want to show in the DOM an specific product. When adding the ItemDetailContainer to app.js, the page doesn't show anything (including the navbar) . Here's my code:
ItemDetailContainer.jsx:
const {products} = require('../utils/data');
const ItemDetailContainer = () => {
const [arrayList, SetArrayList] = useState({});
useEffect(() => {
customFetch(2000, products[0])
.then(result => SetArrayList(result))
.catch(err => console.log(err))
}, [])
return (
<div>
<ItemDetail products={arrayList}/>
</div>
);
}
here's my array in data.js that i want to access:
const products = [
{
id: 1,
image: "https://baltimore.com.ar/img/articulos/4188.png",
title: "Vino Abras Malbec 750cc",
price: 2.500,
stock: 8,
initial: 1
}
ItemDetail.jsx:
const ItemDetail = ({product}) => {
return(
<>
{product.image}
{product.title}
</>
)
}
CustomFetch.js:
let is_ok = true
let customFetch = (time, array) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (is_ok) {
resolve(array)
} else {
reject("error")
}
}, time)
})
}
and my app.js:
function App() {
return (
<div>
<Navbar/>
<ItemDetailContainer/>
</div>
);
}
I've done all the imports and exports, but I dont' know what's the problem.
Since you are passing a product prop in <ItemDetail product={arrayList}/> within ItemDetailContainer make sure you destructure your props correctly in ItemDetail component,like so :
const ItemDetail = ({product}) => {
return(
<>
{product.image}
{product.title}
</>
)
}
Which is basically like this :
const ItemDetail = (props) => {
return (
<>
{props.product.image}
{props.product.title}
</>
);
};
check it out in code sandbox

TextInput gets unfocused after typing each character

I'm using React to build a form and I'm trying to filter a list with the SearchInput (which works the same as TextInput) located in the child component Header. But everytime I type a character the SearchInput gets unfocused
function index() {
const list = [//data\\]
const [search, setSearch] = useState("");
const [filteredResults, setFilteredResults] = useState([]);
const searchItems = (searchValue) => {
setSearch(searchValue);
if (search !== "") {
const filteredData = partners.filter((item) => {
return Object.values(item)
.join("")
.toLowerCase()
.includes(search.toLowerCase());
});
setFilteredResults(filteredData);
} else {
setFilteredResults(partners);
}
};
const Header = () => (
<Box>
<SearchInput
placeholder="Search"
value={search}
onChange={(e) => searchItems(e.target.value)}
/>
</Box>
);
return (
<Parent
headerContent={<Header />}
>
<Box>
<Table data={search.length > 1 ? filteredResults : list} />
</Box>
</Parent>
);
}
export default index;
Oh, I think I can see the problem now - it's the way you're rendering the <SearchInput /> component. You're inadvertantly creating a new functional component on every render. Either inline the Header directly into the Parent control's headerContent property, or create an entirely separate component:
const Header = ({ search, onSearchChange }) => {
const handleChange = (e) => onSearchChange(e.target.value);
return (
<Box>
<SearchInput
placeholder="Search"
value={search}
onChange={handleChange}
/>
</Box>
);
}
function index() {
// ----- 8< -----
return (
<Parent
headerContent={<Header search={search} onSearchChange={searchItems} />}
>
{/* ... */}
</Parent>
);
}
While you're there, you have a subtle bug with your comparison - it looks like you're searching your partners effectively as a list of strings; but, since you're joining them, if you had partners with the names:
'one'
'two'
You're creating a search string as 'onetwo' - so searching for 'et' would match, even though you don't actually have a partner matching that. You can fix that by just checking each partner individually... something like:
const searchItems = (searchValue) => {
setSearch(searchValue);
if (search !== "") {
const searchValueLower = searchValue.toLowerCase();
const filteredData = partners.filter((item) => {
return Object.values(item)
.some(item => item.toLowerCase().includes(searchValueLower);
});
setFilteredResults(filteredData);
} else {
setFilteredResults(partners);
}
};

Separating two Props React

I am working with 2 APIs. The data of the Api's is almost identical except for the name of the theatre and the price to see a movie. I'm currently mapping through and sending the props to my MovieComponent, but the issue is that the prop names are the same and I need to get another value for the {Price} for one cinema. Currently it is providing data from filmWorld and I need to display the cinemaWorld {Price} also. I'm wondering how to separate these two props so I can get the data on cinemaWorld Price.
I can access this data from App.js by logging cinemaWorld.Movies[0].Price (Although I'll need to map through to get different Prices depending on the movie).
App.js:
function App() {
const [filmWorld, setFilmWorld] = useState([]);
const [cinemaWorld, setCinemaWorld] = useState([]);
useEffect(() => {
const theaters = ["cinema", "film"];
theaters.forEach((theater) => {
async function getTheater() {
const data = await api.getMoviesData(theater);
if (data.Provider === "Film World") {
setFilmWorld(data);
} else {
setCinemaWorld(data);
}
}
getTheater();
});
}, []);
const movies = useMemo(() => {
if (!filmWorld.Provider) return [];
return filmWorld.Movies.map((movie, index) => ({
...movie,
cinemaWorldPrice:
cinemaWorld.Provider && cinemaWorld.Movies[index]?.Price,
}));
}, [filmWorld, cinemaWorld]);
return (
<Container>
<Header>
<AppName>
<MovieImage src={movieicon1} />
Prince's Theatre
</AppName>
</Header>
<MovieListContainer>
{movies.map((movie, index) => (
<MovieComponent key={index} movie={movie} />
))}
</MovieListContainer>
</Container>
);
}
and my movieComponent:
const MovieComponent = (props) => {
const { Poster, Price, Title } = props.movie;
return (
<MovieContainer>
<CoverImage src={Poster} alt="" />
<MovieName>{Title}</MovieName>
<InfoColumn>
<MovieInfo>FilmWorld: ${Price}</MovieInfo>
<MovieInfo>CinemaWorld: ${Price}</MovieInfo>
</InfoColumn>
</MovieContainer>
);
};

How do I use Async with array.map when rendering in ReactJS?

How would I go about running this async with ReactJS?
{array.map((content, index) => {
const var = await asyncFunction(param)
return(
<div key={index} className="someClass">
<h4 className="anotherClass">{var}</h4>
</div>
)
})}
You have to separate the async processing from the rendering (as of React 17, at least), and you have to use the promise directly, but can be done in a function, stored in a state variable, then rendered. That function can be called when the array changes using useEffect.
Example:
function MyComponent(array) {
const [vars, setVars] = useState([]);
function handleArrayChanged(newArray) {
// note: no error handling, do this yourself as appropriate.
Promise.all(array.map(var => asyncFunction(var)))
.then((newVars) => setVars(newVars))
.catch(error => { /* handle error */ });
}
useEffect(() => {
handleArrayChanged(array);
}, [array]);
return (
<>
{vars.map((var, index) => {
return(
<div key={index} className="someClass">
<h4 className="anotherClass">{var}</h4>
</div>
)
})}
</>
}
Depending on exactly what asyncFunction does, you may want to break that async check out into its own component, as well, but that's subjective based on what asyncFunction does.
function MyComponent({element}) {
const [var, setVar] = useState(undefined)
function handleElementChanged(newElement) {
asyncFunction(newElement)
.then(newVar => setVar(newVar))
.catch(error => { /* handle error */ });
}
useEffect(() => {
handleElementChanged(element);
}, [element]);
return (
<div className="someClass">
<h4 className="anotherClass">{var}</h4>
</div>
);
}
function MyList({array}) {
return (
<>
{array.map(element, index) => (
<MyComponent
key={index}
element={element}
/>
)}
</>
);
}

Mutation fires twice with react-adopt

I am using react-adopt with react-apollo. The simple reason for this is that having a large number of queries and mutations on a single page gets very messy.
Issue
When firing mut1 only one mutation will happen.
When firing mut2,mut2 fires and then mut1 fires once mut2 finishes.
If I had a mu3,4,5 etc and I clicked mut3 it would run mut3 > mut2and
the finally mut1 If you ran mut5 it would then do 4 > 3 > 2 > 1 etc...
Component
const mut1 = () => {
return (
<Mutation mutation={MUTATION} {...props} />
)
}
const mut2 = () => {
return <Mutation mutation={MUTATION2} {...props} />
}
export default
adopt({
mut1,
mut2,
}
)
I would of course like each mutation to only fire once when clicked.
create MutationContainer.js which contain all your mutation export MutationContainer as a variable
import { gql } from 'apollo-boost'
import { adopt } from 'react-adopt'
const mutation1 = () => (
<Mutation mutation={MUTATION1} {...props} />
)
const mutation2 = () => (
<Mutation mutation={MUTATION2} {...props} />
)
const mutation3 = () => (
<Mutation mutation={MUTATION3} {...props} />
)
export const MutationContainer = adopt({
mutation1,
mutation2,
mutation3
})
Import it on any component where you want to use this mutations
on your Component1.jsx
import { adopt } from 'react-adopt'
import { Value } from 'react-powerplug'
import { MutationContainer } from './MutationContainer'
const Composed = adopt({
input: <Value initial="" />,
container: <MutationContainer />,
})
export const Component1 = () => (
<Composed>
{({ container: { mutation1 }, input }) => {
const handleAdd = ev => {
mutation1.mutation({ variables: { ...variables } })
input.setValue('')
ev.preventDefault()
}
return (
<Form onSubmit={handleAdd}>
<Input
type="text"
value={input.value}
onChange={ev => input.setValue(ev.target.value)}
/>
<Button type="submit">Let's call mutation1</Button>
</Form>
)
}}
</Composed>
)
On Component2.jsx
import { adopt } from 'react-adopt'
import { Value } from 'react-powerplug'
import { MutationContainer } from './MutationContainer'
const Composed = adopt({
input: <Value initial="" />,
container: <MutationContainer />,
})
export const Component2 = () => (
<Composed>
{({ container: { mutation2, mutation3 }, loading }) => {
const handleMutation2 = async () => {
await mutation2.mutation({ variables: { ...variables } })
}
const handleMutation3 = async () => {
await mutation3.mutation({ variables: { ...variables } })
}
return (
<React.Fragment>
<Button onClick={handleMutation2}>
Let's call mutation2
</Button>
<Button onClick={handleMutation3}>
Let's call mutation3
</Button>
<React.Fragment>
)
}}
</Composed>
)
Please take care of variables as par your requirement
And most important point based on your condition
export const App = () => (
<MutationContainer>
{({ data: { loading, data } }) => (
<React.Fragment>
<Component1 {...this.props }/>
<Component2 {...this.props } />
</React.Fragment>
)}
</MutationContainer>
)
If you persist this problem please let me know. will try react-adopt with new approach.

Categories

Resources