How to pass a function to a different screen - javascript

I have 2 screens that I need to share a function ie
//ScreenOne.js
import ScreenTwo from "./ScreenTwo"
...{...
function handlePress(){
Alert.alert(
"",
"Clicked",
[{text: "ok", onPress: ()=>null}]
)
}
return(
<>
<FlatList
data={source}
renderItem={({ item }) => <ScreenTwo item={item} />} />
<Button onPress={handlePress}/>
</>
)
...}...
//ScreenTwo.js
...{...
return(
<>
<Text>{...}</Text>
<Button onPress={handlePress}/>)
</>
...}...
I'm working with functional components. Most of the materials I read only talk of passing data and not methods/functions to other screens. Using this example how can I call the handlePress from ScreenOne to Two...

I'm assuming you are talking about sharing the handlePress function.
You can pass a function in the props:
<ScreenTwo item={item} handlePress={handlePress}/>
And then use it in ScreenTwo
const ScreenTwo = ({handlePress, ...rest}) => {
...
<Button onPress={handlePress}/>)
...
}

Related

Questions about antd's unique key and component structure

I'm working on a project using react, next.js, antd.
However, the following error occurred:
Warning: Each child in a list should have a unique "key" prop.
We know that the following error occurs because the list's child element does not have a unique key.
So, I tried to fix the code where the error occurred by using the following methods, but it was not resolved.
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item key={item.id}>
<BoardCard post={item} />
</List.Item>
)}
/>
</section>
)
};
------------------------------
try 0.
<List.Item>
<BoardCard post={item} key={item.id} />
</List.Item>
try 1.
<List.Item key={item.id}>
<BoardCard post={item} />
</List.Item>
try 2.
renderItem={(item, i) => (
<List.Item key={i}>
<BoardCard post={item} />
</List.Item>
try 3.
<List.Item>
<div key={item.id}>
<BoardCard post={item} />
</div>
</List.Item>
try 4.
<div key={item.id}>
<List.Item>
<BoardCard post={item} />
</List.Item>
</div>
So I start to doubt whether the above problem is a problem with the component structure.
The component where the problem occurred consists of the following three components.
// pages/profile
import React from 'react';
import NicknameEditForm from './NicknameEditForm';
import MyScrap from './MyScrap';
import MyBoard from './MyBoard';
const MyInfo = () => {
return (
<section>
<NicknameEditForm /> // A component that changes a member's nickname
<MyScrap /> // Gets the post that has been liked from the POST table.
<MyBoard /> // I get the post I wrote from the POST table.
</section>
)
};
export default MyInfo;
// MyScrap.js
const { likedPosts } = useSelector((state) => state.post);
// MyBoard.js
const { boardPosts } = useSelector((state) => state.post);
Also, MyScrap and MyBoard components receive the following data from the backend and use it.
// likedPosts used in MyScrap component
likedPosts = [{id: 1, title: 'title1',.....}, [...] ...]
// boardPosts used in MyBoard component
boardPosts = [{id: 1, title: 'title1',.....}, [...] ...]
So, I wonder if the following problem occurred because the key values of MyScrap component and MyBoard component overlap due to the following structure on one page or if there is another reason.
Try using rowKey in List component. You can check the example here
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
rowKey={(item) => item.id}
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item>
<BoardCard post={item} />
</List.Item>
)}
/>
</section>
)
};

How do I show data on one card when hovering and not on all?

Okay there is definitely a quick solution for this I just can't figure out.
Just a description of what I am trying to do:
Whenever I hover over a certain card, I would like to see the description of that item and only that item. But instead what's obviously happening, as you can see from my code, is every single cards description is showing.
I rewrote a simpler version of the code by taking out any unnecessary pieces. Everything is imported correctly, styling and classNames were removed as well.
export function Items() {
const [items, setItems] = useState([])
const [isHovering, setIsHovering] = useState(false)
useEffect(() => {
setItems(Data)
}, [])
function handleMouseOver() {
setIsHovering(true)
}
function handleMouseOut() {
setIsHovering(false)
}
return(
<div>
{items.map(item => {
return(
<Card onMouseOver={handleMouseOver} onMouseOut={handleMouseOut} key={item.id}>
{isHovering ?
<Card.Body>
<p>{item.item_description}</p>
</Card.Body>
:
<Card.Body>
</Card.Body>
}
<Card.Footer>
</Card.Footer>
</Card>
)
})}
</div>
)
}
As far as I can see you don't need to put this logic into parent component, and also it makes everything more complex, since it's hard to manage hovering. I would create new chlid component and manage this state out there internally.
export function Item({item}) {
const [isHovering, setIsHovering] = useState(false);
useEffect(() => {
setItems(Data);
}, []);
function handleMouseOver() {
setIsHovering(true);
}
function handleMouseOut() {
setIsHovering(false);
}
return (
<Card onMouseOver={handleMouseOver} onMouseOut={handleMouseOut}>
{isHovering ? (
<Card.Body>
<p>{item.item_description}</p>
</Card.Body>
) : (
<Card.Body></Card.Body>
)}
<Card.Footer></Card.Footer>
</Card>
);
}
export function Items() {
const [items, setItems] = useState([]);
return (
<div>
{items.map(item => (
<Item key={item.id} item={item} />
))}
</div>
);
}
Your "isHovering" state should also be an array, where you store the hover state for every card. Then on hover set "isHovering" to true only for the right card.

How to use Forwarding Refs with react-router-dom

I already understand the concept of Forwarding Refs and react-router-dom. But in this implementation, I'm not sure how to use it correctly.
I have a child component, where there is a function that set null in a useState.
I want this function to be executed every time I click on the menu item that renders this child component.
This menu is mounted in the com List and the Router in the App, as shown in the 3 files below.
Precisely, I don't know where to put the useRef to execute the child function resetMyState, if it's in App.js or or AppBarAndDrawer.js and how to do it.
childComponent.js
...
const MeusAnuncios = forwardRef((props, ref) => {
const [myState, setMyState] = useState(null);
function resetMyState(){
setMyState(null)
}
async function chargeMyState() {
await
...
setMyState(values)
...
}
...
AppBarAndDrawer.js
...
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List>
{[
{ label: "Minha Conta", text: "minhaConta", icon: "person" },
{ label: "Novo Anúncio", text: "novoAnuncio", icon: "queue_play_next" },
{ label: "Meus Anúncios", text: "meusAnuncios", icon: "dvr" },
{ label: "Estatísticas", text: "estatisticas", icon: "line_style" },
{ label: "Faturamento", text: "faturamento", icon: "local_atm" },
{ label: "childComponent", text: "childComponent", icon: "notifications" },
].map(({ label, text, icon }, index) => (
<ListItem
component={RouterLink}
selected={pathname === `/${text}`}
to={`/${text}`}
button
key={text}
disabled={text !=='minhaConta' && !cadCompleto ? true : false}
onClick={() => {click(text) }}
>
<ListItemIcon>
<Icon>{icon}</Icon>
</ListItemIcon>
<ListItemText primary={label.toUpperCase()} />
</ListItem>
))}
</List>
<Divider />
</div>
);
return(
...
{drawer}
...
)
...
App.js
...
export default function App() {
const childRef = useRef();
...
<Router>
<AppBarAndDrawer/>
<Switch>
<Route path="/childComponent">
<childComponent />
</Route>
...
...
The ref you create does need to reside in a common ancestor, i.e. App, so it and a callback can be passed on to children components. The ref to ChildComponent and the callback to the AppBarAndDrawer. Additionally, the ChildComponent will need to use the useImperativeHandle hook to expose out the child's resetMyState handler.
MeusAnuncios
Use the useImperativeHandle hook to expose out the resetMyState handler.
const MeusAnuncios = forwardRef((props, ref) => {
const [myState, setMyState] = useState(null);
function resetMyState(){
setMyState(null);
}
useImperativeHandle(ref, () => ({
resetMyState,
}));
async function chargeMyState() {
await
...
setMyState(values)
...
}
...
});
App
Create a resetChildState callback and pass the ref to the child component and callback to the AppBarAndDrawer component.
export default function App() {
const childRef = useRef();
const resetChildState = () => {
if (childRef.current.resetMyState) {
childRef.current.resetMyState();
}
};
...
<Router>
<AppBarAndDrawer onClick={resetChildState} /> // <-- pass callback
<Switch>
<Route path="/childComponent">
<ChildComponent ref={childRef} /> // <-- pass ref
</Route>
...
</Switch>
...
</Router>
}
AppBarAndDrawer
Consume and call the passed callback.
const AppBarAndDrawer = ({ onClick }) => { // <-- destructure callback
...
const drawer = (
<div>
...
<List>
{[
...
].map(({ label, text, icon }, index) => (
<ListItem
component={RouterLink}
selected={pathname === `/${text}`}
to={`/${text}`}
button
key={text}
disabled={text !=='minhaConta' && !cadCompleto}
onClick={() => {
click(text);
onClick(); // <-- call callback here
}}
>
<ListItemIcon>
<Icon>{icon}</Icon>
</ListItemIcon>
<ListItemText primary={label.toUpperCase()} />
</ListItem>
))}
</List>
...
</div>
);
...
};

React.FC call with multiple parameter

I am trying to call the function with three parameter but its not working.
Here is the React.FC:
const FormStructure: React.FC<{ record: ModelClass, question: Array<ModelClass>, dropdownItems: Array<ModelTypeClass> }> = ({
record, question, dropdownItems,
}) => {
...
}
and this is the function call:
return (
<Modal
visible={this.state.modalVisible}
onCancel={() => this.setState({ modalVisible: false })}
>
{FormStructure(record, this.state.question, this.state.dropdownItems)}
</Modal>
)
Following Error Message:
"Expected 1-2 arguments, but got 3"
Since FormStructure is a component, you shouldn't be calling it as a function. You should instead create a JSX element:
<Modal
visible={this.state.modalVisible}
onCancel={() => this.setState({ modalVisible: false })}
>
<FormStructure
record={record}
question={this.state.question}
dropdownItems={this.state.dropdownItems}
</FormStructure>
</Modal>
If, hypothetically, you wanted to call it as a function (again, you shouldn't do that since this is a component), the way to conform to the type definition is to pass in an object with three properties:
FormStructure({
record: record,
question: this.state.question,
dropdownItems: this.state.dropdownItems,
});
FormStructure is a Functional Component, so you better render it with the following syntax (JSX):
return (
<Modal
visible={this.state.modalVisible}
onCancel={() => this.setState({ modalVisible: false })}
>
<FormStructure record={record} question={this.state.question} dropdownItems={this.state.dropdownItems)} />
</Modal>
)

Invalid Hook Call - React Hooks

I'm really new to JS and React. I get this error:
Invalid Hook Call
when I try to make a component appear and disappear when another component is clicked. This is my code:
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header title={item.title} style={styles.header} press={setListState(!listState)}/>
{dataToShow}
</View>
)}
EDIT
RenderItem is used in a flat list element as a function. (From what I understand)
const SettingsSection = (props) => {
const db = props.data;
return(
<View>
<FlatList
style={styles.sectionList}
data={db}
renderItem={RenderItem}
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
</View>
);
}
renderItem, as the name suggests, is a render prop, and as such is called directly (like so: renderItem({item})), not instantiated as a component (like so: <RenderItem item={item}/>).
This translates to React not creating the appropriate rendering "context" for hooks to work. You can make sure your RenderItem function is instantiated as a component by using it like this on the render prop:
<FlatList
style={styles.sectionList}
data={db}
renderItem={item => <RenderItem {...item}/>} // see here!
keyExtractor={item=>item.title}
ItemSeparatorComponent={FlatListItemSeparator}
/>
That way, RenderItem is treated as a component and thus can use hooks.
I think problem is occurring due to setListState(!listState) with press. I suggest you to wrap your state changing method into a function. Because onPress accepts only function type but you are giving it a return statement from hooks.
const RenderList = ({data}) => {
return data.map((option, index) => {
return <Item title={option}/>
});
};
const Header = ({ title, style, press }) => (
<TouchableHighlight onPress={press}>
<Text style={style} >{title}</Text>
</TouchableHighlight>
)
const RenderItem = ( {item} ) => {
console.log(styles)
let dataToShow;
const [listState, setListState] = useState(true);
if (listState){
dataToShow = <RenderList data={item.data}/>
} else {
dataToShow = <Text/>
}
return (
<View style={styles.section}>
<Header
title={item.title}
style={styles.header}
press={()=>{
setListState(!listState)
}}
/>
{dataToShow}
</View>
)}

Categories