React Native Button onPress function and props concern - reactjs

I have React Native project. I am new to React. I was told not to put functions in the jsx as it's bad practice. Looking for guidance to keep component clean
// App.js
export const App = () => {
// other things
return (
<CustomButton onButtonPress={()=> someFunction} />
)
}
Then my custom component that I am in question. Should I create a functions which will included the prop or do I use the prop directly in the jsx
export const CustomButton = (props) => {
<Button
onPress={() => props.onButtonPress(item.socketId)}>
Decline
</Button>
or
export const CustomButton = (props) => {
const handleDisconnect = (socketId) => {
props.onButtonPress(socketId)
}
<Button
onPress={() => handleDisconnect(item.socketId)}>
Decline
</Button>

Long story short. React use function component, if you not use memo, your function will be create again and take some memory and time. Also arrow function will recreate too.
import React, { memo, useCallback } from "react";
import { Text, Button } from "react-native";
const ClearCustomButtom = memo(({ onPress, title }) => (
<Button onPress={onPress}>
<Text>{title}</Text>
</Button>
));
const ExtraCustomButtom = memo(({ onPress, title, socketId }) => {
const handlePress = () => {
console.log("Some extra action");
onPress(socketId);
};
return (
<Button onPress={handlePress}>
<Text>{title}</Text>
</Button>
);
});
const RequestCustomButtom = memo(({ url, title }) => {
const handlePress = () => {
//send request on server
};
return (
<Button onPress={handlePress}>
<Text>{title}</Text>
</Button>
);
});
export default function App() {
//UseCallback create point for put in button props, not function but point. Without useCallback component will rerender every time.
const handlePressClearDecline = useCallback(() => {}, []);
const handlePressExtraDecline = useCallback((socketId) => {
console.log(socketId);
}, []);
return (
<>
<ClearCustomButtom
title="ClearCustomButton"
onPress={handlePressClearDecline}
/>
<ExtraCustomButtom
title="ExtraCustomButton"
onPress={handlePressExtraDecline}
socketId="777"
/>
<RequestCustomButtom title="Send request" />
</>
);
}
This example of most common case in react-native, how to write pressable component.

Related

Is it ok to use react state in render prop?

I have two components App and MyComponent, where MyComponent is used in App.
import { useState } from "react";
import { MyComponent } from "./myComponent";
export const App = () => {
const [state, setState] = useState(0);
return (
<>
<MyComponent
render={() => (
<button onClick={() => setState((prev) => prev + 50)}>{state}</button>
)}
/>
</>
);
}
export const MyComponent = (props) => {
const Content = props.render;
return (
<div>
<Content/>
</div>
);
};
Is it ok to use state in the return value of the render prop? Is it considered anti-pattern?
Is it ok to use react state in render prop?
Yes, but... why? children prop was created to achieve exactly what you want here.
<MyComponent>
<button onClick={() => setState((prev) => prev + 50)}>{state}.</button>
</MyComponent>
export const MyComponent = ({ children }) => (
<div>
{children}
</div>
);

Controlling one element inside an array

I'm trying to create an edit feature to my todo-list but i'm kind of stuck and receiving a weird behaviour.
I'm filtering the array using the id's but what happens is that the entire array is changing instead of one element inside of it.
What supposed to happen is when clicking the edit button, the element im clicking on should change to an input (not the entire array)
thanks for any kind of help!
App:
import React, { useState } from "react";
import Header from "./UI/Header";
import TodoList from "./Components/TodoList";
import AddTodo from "./Components/AddTodo";
import { v4 as uuidv4 } from "uuid";
function App() {
const [todos, setTodos] = useState([]);
const [editTodo, setEditTodo] = useState(false);
const onAddHandler = (text) => {
setTodos([
...todos,
{
name: text,
id: uuidv4(),
},
]);
};
const deleteTodoHandler = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
const editTodoHandler = (id) => {
todos.filter((todo) => todo.id === id);
setEditTodo(!editTodo);
};
return (
<div>
<div className="App">
<AddTodo onAddHandler={onAddHandler} />
<Header />
<TodoList
todos={todos}
deleteTodoHandler={deleteTodoHandler}
editTodoHandler={editTodoHandler}
editTodo={editTodo}
/>
</div>
</div>
);
}
export default App;
TodoList.js :
import React, { useState } from "react";
import Todo from "./Todo";
const TodoList = (props) => {
return (
<Todo todo={props.todo}>
{props.todos.map((todo) => {
return (
<p>
{props.editTodo ? <input /> : <span>{todo.name}</span>}
<button onClick={() => props.deleteTodoHandler(todo.id)}>
Delete
</button>
<button onClick={() => props.editTodoHandler(todo.id)}>Edit</button>
</p>
);
})}
</Todo>
);
};
export default TodoList;
When you set the editTodo property to true, the TodoList component re-renders and loops through the todo array again, changing every <span> to an <input>. You're going to have to pass the id of the todo that you want to edit, and add a condition to only change that single item to an <input>.

React: How to refresh a component with onClick using hooks

I'm pretty new on this.
As an exercise I did an App that renders images of cats when clicking on a button (the images are from an API and that works fine).
My idea was to make the button refresh new images when pressed, and I know I have to be using hooks, but I'm not sure if I should use useState, setState or something else.
Here is the code
import React, { useState } from "react";
import { CatsGrid } from "./components/CatsGrid";
const RandomCatApp = () => {
return (
<div className="catContainer">
<h1>Random kittens</h1>
<button onClick={'Some code here'} className="catBtn">
Generate random kitten
</button>
<CatsGrid />
</div>
);
};
export default RandomCatApp;
The button must refresh component to show new images from the API.
CatsGrid component works fine, I just tested it. My problem is with the "onClick" and useState or something else in the code above.
Here is CatsGrid component just in case:
import React, { useState, useEffect } from "react";
export const CatsGrid = () => {
const [imagen, setImagen] = useState([]);
useEffect(() => {
getCats();
}, []);
const getCats = async () => {
const url = "someApiKey";
const resp = await fetch(url);
const data = await resp.json();
const catImg = data.map((img) => {
return {
id: img.id,
url: img.url,
};
});
setImagen(catImg);
};
return (
<div className="imgContainer">
{imagen.map(({ id, url }) => (
<img className="catImg" key={id} src={url} alt="" />
))}
</div>
);
};
Ok, assuming the fetch in getCats in CatsGrid always returns a new set of data then I suggest just using a React key on the CatsGrid component so React will unmount/mount a new instance of it. When the React key changes React will interpret this as a new component to render.
const RandomCatApp = () => {
const [catsKey, setCatsKey] = React.useState(0);
return (
<div className="catContainer">
<h1>Random kittens</h1>
<button
onClick={() => setCatsKey(key => key + 1)}
className="catBtn"
>
Generate random kitten
</button>
<CatsGrid key={catsKey} />
</div>
);
};

React useState() & useContext() - TypeError: setState is not a function - What am I doing wrong?

I've got a few React functional Components that I would like to share a state. In this example two toggle buttons that would conditionally show/hide a searchbar and a navbar.
--Solution, based on the accepted answer, on the bottom--
I'm completely new to useContext() and I keep running into the following error in the console:
Uncaught TypeError: setSearchbarToggle is not a function This goes for both buttons.
Bellow I have a filtered example code. It is just for the example I use the states in one file. In real life I would re-use the states in multiple functional components.
This is my header.js
import React, { useState, useContext } from "react"
import "./header.sass"
import { Context } from "./HeaderContext"
export const Header = () => {
const headerContext = useContext(Context)
const { navbarToggle, setNavbarToggle, searchbarToggle, setSearchbarToggle } = headerContext
return (
<React.Fragment>
<div className={"sticky-top"}>
<button onClick={ () => setNavbarToggle( !navbarToggle )}> Toggle Menu </button>
<button onClick={ () => setSearchbarToggle( !searchbarToggle )}> Toggle Search </button>
{navbarToggle && <h3>Menu is showing</h3>}
{searchbarToggle && <h3>Searchbar is showing</h3>}
</div>
</React.Fragment>
)
}
export default Header
And this is my HeaderContext.jsx
import React, { createContext, useState } from "react";
import PropTypes from "prop-types";
export const Context = createContext({});
export const Provider = props => {
const {
navbarToggle: initialNavBarToggle,
searchbarToggle: initialSarchbarToggle,
children
} = props;
const [navbarToggle, setNavbarToggle] = useState(initialNavBarToggle);
const [searchbarToggle, setSearchbarToggle] = useState(initialSarchbarToggle);
const headerContext = {
navbarToggle, setNavbarToggle,
searchbarToggle, setSearchbarToggle
};
return <Context.Provider value={headerContext}>{children}</Context.Provider>;
};
export const { Consumer } = Context;
Provider.propTypes = {
navbarToggle: PropTypes.bool,
searchbarToggle: PropTypes.bool
};
Provider.defaultProps = {
navbarToggle: false,
searchbarToggle: false
};
I hope you can shed some light on this for me
--edit--
This is my code based on the accepted answer.
import React, { useContext } from "react"
import { Provider,Context } from "./HeaderContext"
export const HeaderWithContext= () => {
const headerContext = useContext(Context)
const { navbarToggle, setNavbarToggle, searchbarToggle, setSearchbarToggle } = headerContext
return (
<React.Fragment>
<div className={"sticky-top"}>
<button onClick={ () => setNavbarToggle( !navbarToggle )}> Toggle Menu </button>
<button onClick={ () => setSearchbarToggle( !searchbarToggle )}> Toggle Search </button>
{navbarToggle && <h3>Menu is showing</h3>}
{searchbarToggle && <h3>Searchbar is showing</h3>}
</div>
</React.Fragment>
)
}
export const Header = () => {
return (
<Provider>
<HeaderWithContext/>
</Provider>
)
};
One of the parent components, e.g. App, must wrap the header (or one of its ancestor components) with Context.Provider:
import { Provider } from "./HeaderContext"
...
<Provider>
<Header />
</Provider>

React component does no re-render when it is swapped with the same one

Please consider the following code:
import React from "react";
import "./styles.css";
const Component = ({ title }) => {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log("Mounted");
}, []);
return (
<div>
<h2>{title}</h2>
<p>{count}</p>
<button onClick={() => setCount(c => c + 1)}>count up</button>
</div>
);
};
export default function App() {
const [index, setIndex] = React.useState(0);
const changeComponent = () => {
setIndex(c => (c === 1 ? 0 : 1));
};
const components = [
{
render: () => <Component title="one" />
},
{
render: () => <Component title="two" />
}
];
return (
<>
<button onClick={changeComponent}>toggle component</button>
{components[index].render()}
</>
);
}
https://codesandbox.io/s/mystifying-hermann-si7cn
When you click toggle component, title changes, but component is not unmounted, you can see it because count is not reset.
How to make it so that new component is mounted on toggle component click?
React needs a way to differentiate one component instance from the other. This will fix it
const components = [
{
render: () => <Component key={1} title="one" />
},
{
render: () => <Component key={2} title="two" />
}
];
Its the same reason react requires dynamically rendered lists to have a key prop. It informs react of which component to update.

Resources