I want to extract some variables and the props like this
const MyComponent = (props, {data}) => {
return (
<div data-set={props["data-set"]}>
{data.name}
</div>
)
};
of course this doesn't work. How do I desctructure the data-set value or how do I keep props while destructuring data ??
Because it's only one variable I want to destructure, I could just use
const MyComponent = props => {
const { data } = props // adding this line here
return (
<div data-set={props["data-set"]}>
{data.name}
</div>
)
};
but if it were a bigger or more nested object, it my not be as cool
You are looking for rest:
const MyComponent = (props, { data, ...rest }) => {
return <div data-set={rest["data-set"]}>{data.name}</div>;
};
Related
I have 2 components that pass to the child the prop data, but only one parent pass the prop numPage and the prop setNumPage,
When I try to use the optional sign in the interface it tells me that React.Dispatch<React.SetStateAction> cannot be undefined. I found a solution that is inadequate, use any, can you give me another solution?
First parent
const Home = () => {
const [searchParams] = useSearchParams()
const [numPages, setNumPages] = useState<number>(1)
const url:string = searchParams.get("search") ? `${SEARCH_URL}${searchParams.get("search")}${PAGES}${numPages}` : `${POPULAR_RESULTS}${numPages}`;
const {data, loading} = useFetch<movieApi>(url);
if(loading) return <Sppiner/>
return (
<div>
<Items
data={data}
numPages={numPages}
setNumPages={setNumPages}
/>
</div>
);
};
Second parent
const GenresPage = () => {
const { data, loading } = useFetch<movieApi>(POPULAR_RESULTS);
if(loading) return <Sppiner/>
return (
<div>
<Items data={data} />
</div>
);
};
export default GenresPage;
Child
interface DataProps {
data: movieApi | null;
numPages:number;
setNumPages:React.Dispatch<React.SetStateAction<number>>;
}
const Items = ({ data,numPages,setNumPages }:DataProps) => {}
To the child if I put any or DataProps it works, but I don't wanna do that.
You can change setNumPages inside DataProps to:
setNumPages: (value: number) => void
var items = values.map((x, index) =>
<CustomComponent key={index} />);
This returns JSX.Element[]. Why doesn't it return typeof CustomComponent[]?
With the former, then I can't use items.sort without difficult errors.
It's correct. It returns JSX.Element[] because you are rendering CustomComponent immediately.
If you want to work with element sort/filter or something else you need filter data before render.
values
.filter(x => //filter your values)
.map((x, index) => // render your components
<CustomComponent key={index} />);
yes,
like Egor said, your map result is CustomComponent resuls array.
if you want to your component instance array try some thing like this
export type CustomComponentType = () => JSX.Element;
export const CustomComponent : CustomComponentType = ()=> {
return <div>my componen result</div>
}
export const test : CustomComponentType[] = ()=> {
const array = [1,2,3,4,5,6,7,8,9];
const componentArray = array.map(c => CustomComponent);
return componentArray;
}
I need to visualise a list of data points where each data point has a field to update its value. After all data point values are adjusted, I would would like to get a list of all values.
My problem is here: when I update one value, all data points are rerendered. And if I have a lot of data points, it is getting slow.
From what I understand, there are 2 approaches how to solve the problem:
Store local state at each data point. But then how can I propagate it to the parent component to aggregate it?
Prevent rerendering of data points that were not changed. Should I use useMemo in this case? What is the correct approach?
Here there is the code:
import React from 'react'
import _ from 'lodash'
const numDataPoints = 3
const dataPointsIndices = _.range(0, numDataPoints).map(id => id.toString())
const initDataPointValues = dataPointsIndices.reduce(
(acc, id) => ({ ...acc, [id]: '' }),
{}
)
const DataPoints = () => {
const [dataPointValues, setDataPointValues] =
React.useState(initDataPointValues)
const updateDataValue = (id, { value }) => {
setDataPointValues({
...dataPointValues,
[id]: value,
})
}
return (
<div>
<button onClick={() => console.log(dataPointValues)}>
Log all values
</button>
<div>
{dataPointsIndices.map(id => (
<DataPoint
key={id}
id={id}
value={dataPointValues[id]}
updateDataValue={updateDataValue}
/>
))}
</div>
</div>
)
}
const DataPoint = ({ id, value, updateDataValue }) => {
console.log('RENDER DATA POINT:', id, value)
return (
<div>
{`data point: ${id}`}
<textarea
value={value}
onChange={event => {
const newValue = event.target.value
updateDataValue(id, { value: newValue })
}}
/>
</div>
)
}
export default DataPoints
I'm using useState hook but after changing the state, the component is not rending itself. I don't know what thing I'm missing.
import React, {useState} from 'react'
import List from '#material-ui/core/List'
import ListTile from "./components/ListTile/ListTile"
import style from './App.module.css'
import InputField from "./components/inputField/InputField";
const App = () => {
const [list, setList] = useState([])
const onFormSubmitHandler = (data) => {
list.push(data)
setList(list)
}
return (
<div className={style.outerDiv}>
<h1 className={style.center}>CLister</h1>
<InputField onSubmit={onFormSubmitHandler}/>
<List component="nav">
{list.map((data, index) =>
<ListTile index={index} body={data}/>
)}
</List>
</div>
);
}
export default App
As your list an array a reference type in js. If you modify the list using push
like list.push() it will also modify the original list in your state ,as a result there will be no change in your state.
Example
let list = [1, 2, 3, 4];
let list2 = list;
// if I modify list2 now
list2.push(5);
console.log(list); // list also gets modified as ,they are reference type
So what you can do
const onFormSubmitHandler = (data) => {
let list2=[...list]; // creating a new variable from existing one
list2.push(data)
setList(list2);
}
or
const onFormSubmitHandler = (data) => {
setList(prev=>([...prev,data]));
}
Remember that your state cant be modificate with push, because the way to modificate it is with the method set
Use this code in the method onFormSubmitHandler
const onFormSubmitHandler = (data) => {
setList(list => ([...list, data]))
}
Lastly remember if your form will be submit you need to break it with e.prevent.default()
const onFormSubmitHandler = (data) => {
list.push(data);
setList([...list]);
}
You should try something like this
import List from '#material-ui/core/List'
import ListTile from "./components/ListTile/ListTile"
import style from './App.module.css'
import InputField from "./components/inputField/InputField";
const App = () => {
const [list, setList] = useState([])
const onFormSubmitHandler = (data) => {
list.push(data)
setList(list)
}
return (
<div className={style.outerDiv}>
<h1 className={style.center}>CLister</h1>
<InputField onSubmit={(e) => onFormSubmitHandler(e.target.value)}/>
<List component="nav">
{list.map((data, index) =>
<ListTile index={index} body={data}/>
)}
</List>
</div>
);
}
export default App
You are editing it the wrong way, you should directly give the new values to the setList function and not try to update the list variable. Thats why you have the function, so that you do not update the original value. What you have to do here is use the previous state within the function and the spread operator since its an array and u just want to add an item:
const onFormSubmitHandler = (data) => {
setList(prevList => [...prevList, data])
}
You should look at the list variable as a read-only variable and not attempt to modify it, you modify it through the setList function.
If you want to do some other modifications instead of just adding item:
const onFormSubmitHandler = (data) => {
let listCopy = [...list];
// do something with listCopy
setList(listCopy);
}
In addition, it seems like you are not sending data at all to the function, the way to send data with your function call is to do it with anonymous function in the component:
<Component onSubmit={(e) => { onFormSubmitHandler(e.target.value) }} />
In an older class I have the following;
class ComponentItem extends React.PureComponent {
render() {
const { item, ...other } = this.props;
return (
<>...</>
)
}
};
Now I want to refactor and use hooks, but how can I access the named properties, and the "other properties inside a hook?
I tried
const ComponentItem = (props) => {
const classes = useStyles();
const { item, ...other } = props;
return (
<>...</>
)
}
const ComponentItem = (item, ...other) => {
const classes = useStyles();
console.log(item); // takes all passed in props
console.log(...other);
return (
<>...</>
)
}
What is the best way to do this in a function using hooks?
Probably using the rest parameters, like the following:
const ComponentItem = ({item, ...others}) => {
return <>
{ /* return of component */ }
</>
}
Read further here: Rest parameters
I hope this helps!
Wrap the component with React.memo() (the equivalent of PureComponent), and destructure the components props in params:
const ComponentItem = React.memo(({ item, ...other }) => {
const classes = useStyles();
return (
<>...</>
)
})