I'm trying to pass an Array of Objects(Enums) from a son component to the father. But I'm quite not getting the trick.
Father Component:
const [openDialog, setOpenDialog] = useState(false);
const handleReturn = () => filtros;
const [filtros, setFiltros] = useState<Filtros[]>([]);
useEffect(() => {
setFiltros(handleReturn);
console.log(filtros);
}, [openDialog]);
<FiltrosDialog
openDialog={openDialog}
handleCloseDialog={() => setOpenDialog(false)}
filtrosSelec={filtros}
/>
Son Component (there's a lot of code here but trust me, the state is returning an Array of Filtros):
interface Props {
openDialog: boolean;
handleCloseDialog: () => void;
filtrosSelec: Filtros[];
}
export enum Filtros {
Ligacoes = "Ligações",
Tempo = "Tempo em chamada (min)",
Email = "E-mails enviados",
Reuniao = "Reuniões agendadas",
}
const FiltrosDialog: React.FunctionComponent<Props> = ({
openDialog,
handleCloseDialog,
filtrosSelec,
})=> {
const [checked, setChecked] = useState<Filtros[]>([]);
const [left, setLeft] = useState<Filtros[]>([
Filtros.Reuniao,
Filtros.Tempo,
Filtros.Email,
Filtros.Ligacoes,
]);
const [right, setRight] = useState<Filtros[]>(filtrosSelec);
const leftChecked = intersecao(checked, left);
const rightChecked = intersecao(checked, right);
...}
Basically, I'm not sure if my Filtros[] props is supossed to be a function or an array itself...
Your states are fine, but here's the logic for child/parent communication in terms of asking the child to change parent property.
cont Parent = () => {
const [count, setCount] = useState(0)
return <Child count={count} change={setCount} />
}
You can see the setCount is passed it, not count. A child can only change parent property through a method. Because in terms of setting the property, count = 3 is not going to make it due to that count is a primitive value.
This is normally quite clear when you are creating an input component, such as the following.
<Input value={value} onChange={onChange} />
Just found out how to do it. I need to pass the setState as the function inside the child prop on the parent component. And the child prop need to be a void and its argument is the actual Array:
Parent:
const [filtros, setFiltros] = useState<Filtros[]>([]);
...
<FiltrosDialog
openDialog={openDialog}
handleCloseDialog={() => setOpenDialog(false)}
filtrosSelec={setFiltros} />
Child:
interface Props {
openDialog: boolean;
handleCloseDialog: () => void;
filtrosSelec: (arg: Filtros[]) => void;
}
const FiltrosDialog: React.FunctionComponent<Props> = ({
openDialog,
handleCloseDialog,
filtrosSelec,
}) => {
const [right, setRight] = useState<Filtros[]>([]);
useEffect(() => filtrosSelec(right));
...
Related
I recently started using typescript and I'm having trouble with passing a use state setter function,
my parent component is is this :
const [word, setword] = useState('run')
and in the child im passing :
<Child word={word} setword={setword}/>
the child has this :
interface Props {
word: string
setword: React.Dispatch<React.SetStateAction<string>>;
}
const VoicetoText: React.FC<Props> = (word, setword) => {
return (
{word}
<button onClick={()=>setword('hey buddy')} ><button/>
)}
i keep getting an error :
You forgot to add a curly brace to
const VoicetoText: React.FC<Props> = (word, setword)
either use props or deconstruct the
props
const VoicetoText: React.FC<Props> = (props)
props.setWord
deconstruct
const VoicetoText: React.FC<Props> = ({word, setword})
Just use setword: (word: string) => void as the prop type.
interface Props {
word: string
setword: (word: string) => void;
}
I would suggest to not make your life harder and go wtih something like:
const Child = ({setWord}: {setWord: (arg0: string}) => void) => {
// () => setWord("word") is jsut for sample sake, you should avoid using arrow function calls inside event handleres like this
return <button onClick={() => setWord("word")}>Click me</button>
}
const Parent = () => {
const [word, setWord] = useState(false);
return <Child propName={setWord}/>
}
I have a Modelmenu that is nested in the parent component, it implements the function of opening a modal window by click. Also in this parent component there is a child component to which you need to bind the function of opening a modal window by one more element. I can do this if all the logic is in the parent component, but this is a bad practice, since I will have to duplicate the code on each page in this way. How can I do this? I'm sorry, I'm quite new, I can't understand the callback.
Parent:
const Home: NextPage = () => {
const handleCallback = (handleOpen: any) => {
}
return (
<>
<ModalMenu parentCallback={handleCallback}/>
<Slider handleCallback={handleCallback}/>
</>
)
}
Modal:
export const ModalMenu: FC = (props) => {
const [play, setPlay] = useState<boolean>(false)
const handleOpen = () => {
props.parentCallback(setPlay(!play))
};
const handleClose = () => {
setPlay(false)
setPlay(!play)
};
return
}
Child:
export const Slider: FC= (props) => {
return (
<>
<Image nClick={props.handleCallback}/>
</>
I did as advised in the comments using hook, it works fine, maybe it will be useful to someone. Custom hook is really convenient
export const useModal = () => {
const [play, setPlay] = useState<boolean>(false)
const handleOpen = () => {
setPlay(!play)
};
const handleClose = () => {
setPlay(false)
setPlay(!play)
};
return {
play, handleOpen, handleClose
}
}
If you're looking to pass down values and/or functions from a parent to two or more children, I think it's better to just have the values and functions in the parent
Parent :
const Home: NextPage = () => {
const [play, setPlay] = useState<boolean>(false)
const handleOpen = () => {
setPlay(prev => !prev)
};
const handleClose = () => {
setPlay(false)
setPlay(!play)
};
return (
<>
<ModalMenu handleOpen={handleOpen} handleClose={handleClose} play={play}/>
<MainSlider <ModalMenu handleOpen={handleOpen} handleClose={handleClose} play={play}/>
</>
)
}
if you want to pass an interface to the props in the children for typescript your interface will look something like this
interface iProps {
play : boolean;
handleOpen : () => void;
handleClose : () => void;
}
export const ModalMenu: FC = (props):iProps => {
// you can easily access all you want
const {handleClose, handleOpen, play} = props
return
}
export const Slider: FC= (props): iProps => {
const {handleClose, handleOpen, play} = props
return (
<>
<Image onClick={handleOpen}/>
</>
I have two components, such that they are:
const ChildOn = (/*...*/) => {
//...
}
const Parent = () => {
const [is42, setIs42] = useState(true)
return (is42 ? <ChildOff ... > : <ChildOn ... />)
}
The definition of ChildOff is no important.
I want to define them as either of the following, yet I can't decide which:
Declares functions used in children, based on a variable/function in the parent, inside each child.
type ChildOnProp = { setIs42: Dispatch<SetStateAction<boolean>> };
const ChildOn = ({ setIs42 }: ChildOnProp) => {
const f1 = () => { setIs42(true); };
return <Text onPress={f1} />;
};
const Parent = () => {
return is42
? <ChildOff setIs42={setIs42} />
: <ChildOn setIs42={setIs42} />;
};
Defines functions used by children, inside the parent.
type ChildOnProps = { func: () => void }
const ChildOn = ({ func }: ChildOnProps) => {
return <Text onPress={func} />
}
const Parent = () => {
const [is42, setIs42] = useState(true)
const f1 = useCallback(() => { setIs42(true) })
const f2 = useCallback(() => { setIs42(false) })
return (is42 ? <ChildOff func={f2} /> : <ChildOn func={f1} />)
}
While (1) is much prettier to me, (2) seems a lot more efficient. Yet I don't know if I'm apt to judge that, since I've read many articles on React contradicting each other on when it's best to use useCallback.
The React community warns against useless memoizing as it could add complexity without any of the benefits in some situations.
In this case, I think it's worth memoizing the callbacks since it will reduce the number of unnecessary renders of the child components, which otherwise could have negative impact, or even introduce issues down over time.
To do this, there's a common pattern of creating a custom hook, often called useToggle, which memoize common setters.
import {useState, useMemo} from 'react';
export function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
// Defined once, so guaranteed stability
const setters = useMemo(() => ({
toggle: () => setValue(v => !v),
setFalse: () => setValue(false),
setTrue: () => setValue(true),
setValue,
}), [setValue]);
// Defined each time the value changes, so less than every render.
return useMemo(() => ({
...setters,
value
}), [value, setters]);
}
This can be used in the parent as such:
const Parent = () => {
const { value: is42, setTrue, setFalse } = useToggle(true);
return (is42 ? <ChildOff func={setFalse}> : <ChildOn func={setTrue} />);
}
TypeScript will infer all the types automatically, so it works out of the box.
If there's multiple state values that need a toggle callback, we can easily identify each one by not destructuring.
const firstState = useToggle(true);
const secondState = useToggle(true);
//...
return (
<>
<Foo onClick={firstState.toggle} />
<Bar onClick={secondState.toggle} />
</>
);
As a reference, here's a simpler implementation from Shopify.
As for memoizing too much or too little, Meta (Facebook) is apparently experimenting with memoizing everything by default at transpile time, so that devs wouldn't have to think about it.
I have a component which fetches data and then passes props to a child child component.
This causes and infinite re-render caused by child component. I wonder what is happening
Here is how my code looks like
const Page: FunctionComponent<pageProps> = (): JSX.Element => {
const [userInfo, setUserInfo] = useState<userInfoStruct>();
const [data, setData] = useState<any>();
useEffect(() => {
// fetch info from localstore
setUserInfo(dataFromLocalStorage);
fetchSomeData(); // do stuff... and setData(fetchResult)
}, [userInfo]);
return (
<div>
<ButtonsAndStuff />
<DisplayData data={data} />
</div>
)
};
My child component looks something like this
const DisplayData: FunctionComponent<displayDataProps> = ({ data }): JSX.Element => {
const data_: Array<any> = data.map(d => (d.value))
return (
<div>
{data_.map(i => {
return (
<div>
{i}
</div>
)
})}
</div>
)
};
Unfortunately my component continually re-renders and react says the problem comes from my child component specifically at the level of taking props in the child i.e this line
const DisplayData: FunctionComponent<displayDataProps> = ({ data }): JSX.Element => {/*... */};
I don't know what is going wrong at this point.
temp
useEffect(() => {
let userName: string = localStorage.getItem("userName");
let user: string = localStorage.getItem("user");
if (userName === undefined || user === undefined) {
return;
} else {
setUserInfo({ user: user, userName: userName });
setIsAuth(true);
}
/* */
if (blogData.length < 1) {
fetchBlogData(user, blogIndex).then(result => {
console.log(result)
setBlogData(result);
});
} else {
return;
}
/* */
}, []);
Your problem doesn't seem to be caused by your child component, but by this part of your code:
const [userInfo, setUserInfo] = useState<userInfoStruct>();
const [data, setData] = useState<any>();
useEffect(() => {
// fetch info from localstore
setUserInfo(dataFromLocalStorage);
fetchSomeData(); // do stuff... and setData(fetchResult)
}, [userInfo]);
Assuming that dataFromLocalStorage isn't just a string/number, it's probably a unique array/object every time. You alter userInfo, which therefore makes the [userInfo] dependency list change, therefore re-executing your effect, ad infinitum.
If you only want to execute the effect once, use [] as dependency list.
I am setupping a simple dashboard to challeging my self with ReactJS, but I have some issues preventing useless re-rendering.
I have a root component called App where I fetch some data.
const App = () => {
const [data, setData] = useState(null);
const [list1, setList1] = useState(null);
const [list2, setList2] = useState(null);
const [list3, setList3] = useState(null);
const [list4, setList4] = useState(null);
useEffect(() => {
const fetchData = fetchDataInSomeWay();
const fetchedData = getData(fetchData);
const list1Data = getList1(fetchData);
setList1(list1Data);
setData(fetchedData);
});
...
{ data !== null
&& (
<Parent
data={data}
list1={list1}
list2={list2}
list3={list3}
list4={list4}
/>
};
Then I setup a Parent component where I created some Select component and other elements which depend on the values selected by select.
I have a Select element for each list state created with useState();
const Google = ({
data,
list1,
list2,
list3,
list4,
}) => {
const [typeValue, setTypeValue] = useState('someValue');
const [list1Value, setList1Value] = useState(list1[0]);
const [list2Value, setList2Value] = useState(list2[0]);
const [list3Value, setList3Value] = useState(list3[0]);
const [list4Value, setList4Value] = useState(list4[0]);
const onChangeSelectTypeValue = (value) => {
setTypeValue(value);
};
...
const selectTypeValueElement = (
<SelectElement
select={selectType}
value={[typeValue]}
onChangeValue={onChangeSelectTypeValue}
values={list1Value}
/>
);
...
<div className="interactionHeaderChart">
{ selectTypeValueElement }
...
</div>
};
Then I have a Select element where I do not store a state, but where option selected is passed to Parent compoment.
const SelectElement = ({
select, value, values, onChangeValue,
}) => {
...
<Select
...
value={value[0]}
onChange={onChangeValue}
>
...
};
Now when I select some option from one Select, state of Parent change and all Childs re-render, all Selects components and also other components which depend on the values selected by select.
Can I prevent all Select components from re-rendering? Can I avoid to re-render all other components which does not depend on the values of option selected?
The fact that the state has changed from the onChange function and not from useEffect() is confusing me and I can not understand how to solve it.
Thanks.
You should look into shouldComponentUpdate:
https://reactjs.org/docs/react-component.html#shouldcomponentupdate
Usually, in order to use this with your SelectElement component you will first have to convert it into a Class. You can then add the shouldComponentUpdate function to it and check the previous and next props are the same or not. If they are the same, don't update.
However, if your props are not complex objects, you can actually just recreate your SelectElement as a PureComponent. This will automatically check the props and will not re-render if they're the same.
e.g.
class SelectElement extends React.PureComponet {...
you can use memo to avoid re rendering.
Way 1:
const NestedComponent = () => {
return (
<div>
ContainerComponent
</div>
);
};
export default React.memo(NestedComponent);
Way 2:
function ParentComponent(a, b) {
const childComponent = React.useMemo(() => <ChildComponent posts={a} />, [a]);
return (
<>
{childComponent}
</>
)
}