React Native: onChangeText is not updating hook state - javascript

So I'm trying to do something very simple in my react native application, but it doesn't seem to be working correctly. I have a component in my native application that allows the user to create a post. The user can simply input the text that they would like as the body of their post. Of course I have decided to use the TextInput component for this and I am setting my body state by using onChangeText={text => onChangeText(text)}. When I click the create_post button in the upper right of the header I get "" as a value for the body.
I have other text input components throughout my application that work just as intended but for some reason this one is not. I have looked all over online and no one seems to have this issue. They only have an issue with it in a class component and not a functional component like the one I have here:
import React,{useLayoutEffect,useState} from 'react'
import { TextInput,ScrollView } from 'react-native'
import { Button } from './../components/Button';
const CreatePost = ({navigation,route}) => {
const [body,onChangeText] = useState('');
const [media,setMedia] = useState('');
useLayoutEffect(() => {
navigation.setOptions({
title: 'New Post',
headerRight: () => (
<Button style={{marginRight:40,fontSize:20}} title="Post" textColor="#0d00ff" onPress={create_post} />
)
})
},[])
const create_post = () => {
const timestamp = new Date().toISOString().split('.')[0];
console.log({body}); // expected: 'some text', result:""
const post = {
pid: 0,
username: 'head_honcho',
body: body
}
// makes call to database
navigation.navigate('HomeTab',{screen: 'Home',params:{post:post}});
}
return (
<ScrollView style={{padding:16}}>
<TextInput placeholder="What's happening?" keyboardType="default" value={body} style={{fontSize:20}} onChangeText={text => onChangeText(text)}/>
{/* <TouchableOpacity style={{justifyContent:'center',alignItems:'center', position:'absolute',bottom:0,left:0,right: 0}}>
<FontAwesome5 name="photo-video" size={20} />
<Text>Add Media</Text>
</TouchableOpacity> */}
</ScrollView>
)
}
export default CreatePost
I think this seems pretty straight forward and I feel silly having to ask a question so basic. My only other thought is that it has something to do with useLayoutEffect and that when create_post is called that it only has access to the initial state and not the updated one. If anyone has any ideas as to what I'm doing wrong that would be great. TIA!
EDIT
Just shortly after posting this I decided to see what would happen if I let useLayoutEffect check for updates in my body variable. Sure enough the body of the post actually came through as it should have.
useLayoutEffect(() => {
navigation.setOptions({
title: 'New Post',
headerRight: () => (
<Button style={{marginRight:40,fontSize:20}} title="Post" textColor="#0d00ff" onPress={create_post} />
)
})
},[body])
But I would still like to know why this is? Why doesn't onChangeText set my body variable as mentioned in the react documentation?

Also you can do it like this.
useLayoutEffect(() => {
navigation.setOptions({
title: 'New Post',
})
},[])
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<Button style={{marginRight:40,fontSize:20}} title="Post" textColor="#0d00ff" onPress={create_post} />
)
},[body])
This way only the button is re rendered

Related

react native im trying to change the state of a button but its taking only the ternary else side

im trying to change the state using this but everyTime it takes add function only how to make the state change onPress ..what can i do please let me know
component //
<Searchitems
key={index}
crypto={crypto}
multipletest={multipletest}
remove={crypto => remove(crypto)}
add={crypto => add(crypto)}
// status={status}
removes="Remove"
adds="Add"
/>
const [statusss, setStatus] = React.useState(false);
onPress={() =>
setStatus(!statusss) ? props.remove(crypto) : props.add(crypto)
}
As mentioned in my comment, it is a bit difficult to understand what you are trying to do with your code. But still, try it like this. It appears that you are trying to check condition on a function.
const [statusss, setStatus] = React.useState(false);
useEffect(() => {
statusss ? props.remove(crypto) : props.add(crypto)
}, [statusss]);
<SomeComponent
onPress={() => setStatus(!statusss)}
/>

How to move ID from an API in one component to another componet to use that ID for another API

I will apologize in advance for incorrect lingo, just started to code in Nov.
I am using this API (https://www.themealdb.com/api/json/v1/1/filter.php?i=)
to get idMeal, strMeal, strMealThumb from the objects, which is mapping over cards to populate different recipes. (Search Component)
I want the user to be able to click on it for them to go to another component that uses another API to populate the actual recipe( Recipe Component) with the id from the Search Component.
www.themealdb.com/api/json/v1/1/lookup.php?i=${idMeal}
Unsure how to proceed with this. Was thinking about using a React Router Link to jump to the other component, and need to make the idMeal into state to transfer from Search into Recipe.
Should I use Redux, Context or something else?
export default function Search() {
const [isSearched, setSearched] = useState("");
const [meals, setMeals] = useState([]);
const [isId, setId] = useState("");
const submitHandler = async (e) => {
e.preventDefault();
// console.log(isSearched);
const res = await axios.get(
`https://www.themealdb.com/api/json/v1/1/filter.php?i=${isSearched}`
);
setMeals(res.data.meals);
};
useEffect(() => {}, [meals]);
return (
<PageContainer>
<ForkSpoonImg src={forkspoon} />
<form onSubmit={submitHandler}>
<h2 style={{ textAlign: "center" }}>Please Search an Ingredient</h2>
<Input type="text" onInput={(e) => setSearched(e.target.value)}></Input>
<Button type="submit">
<SearchIcon />
</Button>
</form>
<CardContainter>
{meals.map(({ idMeal, strMeal, strMealThumb }, index) => {
return (
<Tag //<------- will change to Link, was just testing to see endpoint
href={`www.themealdb.com/api/json/v1/1/lookup.php?i=${idMeal}`}
key={index}
>
<Cards key={index} title={strMeal} url={strMealThumb} />
</Tag>
);
})}
</CardContainter>
</PageContainer>
);
}
Kindest Regards!

React render list only when data source changes

Basically I have a modal with a state in the parent component and I have a component that renders a list. When I open the modal, I dont want the list to re render every time because there can be hundreds of items in the list its too expensive. I only want the list to render when the dataSource prop changes.
I also want to try to avoid using useMemo if possible. Im thinking maybe move the modal to a different container, im not sure.
If someone can please help it would be much appreciated. Here is the link to sandbox: https://codesandbox.io/s/rerender-reactmemo-rz6ss?file=/src/App.js
Since you said you want to avoid React.memo, I think the best approach would be to move the <Modal /> component to another "module"
export default function App() {
return (
<>
<Another list={list} />
<List dataSource={list} />
</>
);
}
And inside <Another /> component you would have you <Modal />:
import React, { useState } from "react";
import { Modal } from "antd";
const Another = ({ list }) => {
const [showModal, setShowModal] = useState(false);
return (
<div>
<Modal
visible={showModal}
onCancel={() => setShowModal(false)}
onOk={() => {
list.push({ name: "drink" });
setShowModal(false);
}}
/>
<button onClick={() => setShowModal(true)}>Show Modal</button>
</div>
)
}
export default Another
Now the list don't rerender when you open the Modal
You can use React.memo, for more information about it please check reactmemo
const List = React.memo(({ dataSource, loading }) => {
console.log("render list");
return (
<div>
{dataSource.map((i) => {
return <div>{i.name}</div>;
})}
</div>
);
});
sandbox here

React Todo App I get an error while converting class components to functional components

First of all, thank you in advance for your help. While making Todo App, I made adding and removing operations into functional components, but I could not make other components. I would be glad if you could help.
TodoItem.js: (I tried a lot but could not make it functional due to errors.)
class TodoItem extends Component {
render() {
const { id, title } = this.props.todo // I did not understand here what it does.
return (
<div>
<p>
<input
type="checkbox"
onChange={this.props.markComplete.bind(this, id)} // and here too
/>
{""} {title}{" "}
<button onClick={this.props.deleteTodo.bind(this, id)}>X </button>{" "}
</p>{" "}
</div>
)
}
}
Addtodo.js: (I converted it to functional but it doesn't list the input I wrote.)
const Addtodo = () => {
const [title, setTitle] = useState("")
const onSubmit = (e) => { // I made a mistake here, I don't know why there is a problem.
e.preventDefault()
setTitle("")
}
const onChange = (e) => setTitle(e.target.value)
return (
<form onSubmit={onSubmit}>
<input
type="text"
onChange={onChange}
value={title}
placeholder="Add todo"
/>
<input type="Submit" value="Submit" className="btn" />
</form>
)
}
App.js component: (I was able to make them functional. I would be glad if you can check it.)
const App = () => {
const [todos, setTodos] = useState([])
useEffect(() => {
axios
.get("https://jsonplaceholder.typicode.com/todos")
.then((res) => setTodos(res.data))
}, [])
const markComplete = (id) => {
setTodos(
todos.map((todo) => {
if (todo.id === id) {
todo.completed = !todo.completed
}
return todo
})
)
}
const deleteTodo = (id) => {
axios
.delete(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then((res) => setTodos([...todos.filter((todo) => todo.id !== id)]))
}
// Add Todo
const Addtodo = (title) => {
axios
.post("https://jsonplaceholder.typicode.com/todos", {
title,
completed: false
})
.then((res) => setTodos([...todos, res.data]))
}
return (
<div className="App">
<div className="container">
<Header />
<AddTodo Addtodo={Addtodo} /> // I think I made a mistake here with the props.
<Todo
todos={todos}
markComplete={markComplete}
deleteTodo={deleteTodo}
/>{" "}
</div>{" "}
</div>
)
}
There is a mismatch between your definition of the AddTodo component, and then adding your AddTodo component to the DOM. In Addtodo.js, you've got the function signature as:
const Addtodo = () => {
Therefore the Addtodo component is not expecting any props to be passed into it.
In App.js, you try to add the component to the DOM with:
<AddTodo Addtodo={Addtodo} />
So you're asking the component to be rendered with a prop called Addtodo, but as stated earlier, in the component's definition, it's not expecting to receive any props.
You need to decide whether or not you want / need the AddTodo component to receive props.
If you want it to receive the AddTodo prop, you can change the function definition to:
const Addtodo = ({ AddTodo }) => {
Also, make sure that when you export the Addtodo component, you export it as a default export, as the casing is currently inconsistent in your code (defined as Addtodo, but tried to render as AddTodo in App.js). If this is a default export though, it doesn't matter too much. Make sure your import statement for the AddTodo is the same as when you render AddTodo.
To be explicit, make sure you have export default Addtodo in Addtodo.js
Make sure the import statement for the add todo component is the same. So, if the top of App.js says import AddTodo from './Addtodo', then when you render the component later in the file, it is done like <AddTodo />. And if the import was import Addtodo from './Addtodo', then the component is rendered as <Addtodo> (casing must be consistent)
I apologise if that wasn't the clearest explanation, I'm unsure of what the actual terms for some of the things I referred to are, but I hope you got what I was trying to say.
You can remove the onSubmit function from the form element and use it with onClick in the button element. Give it a try. I think it will work.

How to switch the state of buttons in react hooks after updating it through rest api?

Hey guys I am very new to react and trying to make the frontend of a blog web application. I am able to show the posts on the homepage and I am able to make the like button work without API calls, just with managing states.
Now with API call, the like button shows red(button fills with red) if the post is liked by the user and I am able to unlike it by clicking it, it changes the count and it unlike the post in the backend, but it doesn't change the button state to unlike button and it keeps on unliking it rather than switching to like button state.
If the post is not liked by the user, then the button completely disappears and doesn't show on the screen, so I am not able to like the post.
This is the code I have written, It is not a good way to write react code I think, If anyone can help resolve this issue, it would be highly enlightening as I am still learning. Please do ask for more information if needed.
This is the code.
const [liked, setLiked] = useState(null)
function setlikeCount(post){
return(
post.like_count = post.like_count + 1
)
}
function setunlikeCount(post){
return(
post.like_count = post.like_count - 1
)
}
function likePosts(post) {
console.log('liked the post')
return(
axiosInstance.post('api/posts/' + post.slug + '/like/')
)
}
function unlikePosts(post) {
console.log('unliked the post')
return(
axiosInstance.delete('api/posts/' + post.slug + '/like/')
)
}
{myposts.posts && myposts.posts.results.map((post) => {
return (
<h4>{post.title}</h4>
)
}
{post.likes && post.likes.map((lik, index) => {
console.log(user, lik.id)
return (
user === lik.id ? (<FavoriteRoundedIcon style={{ color: "red" }}
key={index}
onClick={ () =>{
unlikePosts(post)
setunlikeCount(post)
setLiked((liked) => liked===false)
}}
/>)
: (<FavoriteBorderRoundedIcon key={index}
onClick={ () =>{
likePosts(post)
setlikeCount(post)
setLiked((liked)=> liked===true)
}}
/>)
)
})
}
const [myposts, setPosts] = useState({
posts: null,
})
fetching posts
useEffect(() => {
axiosInstance.get('api/posts/myhome').then((res) => {
const allPosts = res.data;
setLoading(false)
setError("")
setPosts({ posts: allPosts })
// console.log(allPosts.results['0'].likes['0']);
})
.catch(() => {
setLoading(false)
setPosts({})
setError('Something went wrong!')
})
}, [setPosts])
In the code, the user has the user's id.
Is it possible to check the condition like user in lik.id than user === lik.id, like how we check conditions in python?
lik looks like this [{id: 1, username: "testuser12"}]
Thanks
You need to show the button based on the content of the array like below
{post.likes && post.likes.find(x=>x.id===user) ?
(<FavoriteRoundedIcon style={{ color: "red" }}
key={index}
onClick={ () =>{
unlikePosts(post)
setunlikeCount(post)
setLiked((liked) => liked===false)
}}
/>)
: (<FavoriteBorderRoundedIcon key={index}
onClick={ () =>{
likePosts(post)
setlikeCount(post)
setLiked((liked)=> liked===true)
}}
/>)
}
If the array has values and the user is part of the array you show red button and if the array is not defined or user is not in the array you show the other button.
Firstly, your setLiked method isn't right. if you want to set it to true/false just call:
setLiked(true)
Secondary, you should init your liked state. Meaning you need to useEffect (when the component loads) and read from your API if post liked or not. But the initial value better to be false and not null.

Categories

Resources