I have multiple component which has state change during its data collection from get api call, are called from a single component get re-render multiple times , Please help to avoid re-render when open this page
const MyData = () => {
const [data, setData] = useState("");
const [newData, setNewData] = useState("");
const getData = () => {
axios.get("url").then(async function (response) {
setData(response);
});
};
const getData2 = () => {
axios.get("url").then(async function (response) {
setNewData(response);
});
};
useEffect(() => {
getData();
getData2();
});
const NewData = () => {
return (
<View>
<Text>{data.name}</Text>
</View>
);
};
const RewData = () => {
return (
<View>
<Text>{newData.name}</Text>
</View>
);
};
return (
<View>
<NewData />
<RewData />
</View>
);
};
You provide no dependency array (undefined) to your useEffect. If the component's state changes, then the useEffect will be called again, which sets the state again, and so on.
You can provide an empty dependency array which will cause the useEffect to be called only once.
useEffect(() => {
getData();
getData2();
}, []);
Edit: In response to the comments. You can prevent the screens content from rendering until the data has been fetched as follows.
const MyData = () => {
const [data, setData] = useState();
const [newData, setNewData] = useState();
const getData = () => {
axios.get("url").then(async function (response) {
setData(response);
});
};
const getData2 = () => {
axios.get("url").then(async function (response) {
setNewData(response);
});
};
useEffect(() => {
getData();
getData2();
}, []);
if (!data || !newData) {
return null
}
const NewData = () => {
return (
<View>
<Text>{data.name}</Text>
</View>
);
};
const RewData = () => {
return (
<View>
<Text>{newData.name}</Text>
</View>
);
};
return (
<View>
<NewData />
<RewData />
</View>
);
};
Related
Here I have a promise inside refresh control method,
const Component = () => {
const [refreshing, setRefreshing] = useState(false);
const onRefresh = useCallback(() => {
setRefreshing(true);
getData();
setRefreshing(false);
}, []);
return (
<ScrollView
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}>...</ScrollView>
);
}
How to wait for the getData() function and after that set refresh to false? Problem is it isn't waiting for the getData() function to finish. (getData() is a promise)
the getData() function,
const getData = () => {
axios.get(`https://somedomain.com/fetch/user/1`).then(res => {
...
}
}
const Component = () => {
const [refreshing, setRefreshing] = useState(false);
const getData = () => {
axios.get(`https://somedomain.com/fetch/user/1`).then(res => {
...
setRefreshing(false);
}
}
const onRefresh = useCallback(() => {
setRefreshing(true);
getData();
}, []);
return (
<ScrollView
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}>...</ScrollView>
);
}
Alternative:
const getData = async () => {
const response = await axios.get(`https://somedomain.com/fetch/user/1`);
//Handle your response here
}
const onRefresh = useCallback(async () => {
setRefreshing(true);
await getData();
setRefreshing(false);
}, []);
getData() is a promise inside a function. In that method, I returned the promise and did like this to solve this issue,
const onRefresh = useCallback(async () => {
setRefreshing(true);
getData().then(() => {
setRefreshing(false);
});
}, []);
I have this simple react native app that on button click it redirects user to a page. I'm saving data in a cache so that if user clicks the store button it stores data and after refresh it sets store data. However, I would like to implement this logic on a single button, not on 2 different buttons as it is now. Can someone explain me how could I achieve this?
export const App = () => {
const [showFirstWeb, setFirstWeb] = useState(false);
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem("#web_Key");
return setFirstWeb(JSON.parse(jsonValue));
} catch (e) {
console.log("error", e);
}
};
useEffect(() => getData, []);
const storeData = async () => {
try {
const jsonValue = JSON.stringify(showFirstWeb);
await AsyncStorage.setItem("#web_Key", jsonValue);
} catch (e) {
console.log("error", e);
}
};
return (
<View>
<View style={styles.buttonStyle}>
<Button onPress={setFirstWeb(!showFirstWeb)}/>
<Button onPress={storeData} title="store"/>
<View>
{showFirstWeb && <MyWebComponent uri="https://www.google.com/" />}
</View>
</View>
);
};
const MyWebComponent = (uri) => {
return <WebView source={uri} />;
};```
export const App = () => {
const [showFirstWeb, setFirstWeb] = useState(false);
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem("#web_Key");
return setFirstWeb(JSON.parse(jsonValue));
} catch (e) {
console.log("error", e);
}
};
// you forgot to call the function here
useEffect(() => getData(), []);
const storeData = async () => {
try {
// get the new value
const newShowFirstWeb = !showFirstWeb
// use the new value
setFirstWeb(newShowFirstWeb)
const jsonValue = JSON.stringify(newShowFirstWeb );
await AsyncStorage.setItem("#web_Key", jsonValue);
} catch (e) {
console.log("error", e);
}
};
return (
<View>
<View style={styles.buttonStyle}>
<Button onPress={storeData} title="store"/>
<View>
{showFirstWeb && <MyWebComponent uri="https://www.google.com/" />}
</View>
</View>
);
};
I don't get any errors. I use flatlist in the home page of my code without any errors but it doesn't render in the second page. It also doesn't display console.log inside renderItem.
const NamazTakibi = () => {
const [ayet, setAyet] = useState('');
const fetchAyet = async () => {
try {
const ayet = await axios.get('https://api.acikkuran.com/surah/6/verse/1?author=14');
setAyet(ayet)
} catch {
console.log("error");
}
}
useEffect(() => {
fetchAyet();
}, []);
const renderItem = ({item}) => {
return (
<Cards item={item} />
)
}
return (
<FlatList
style={styles.container}
data={ayet}
renderItem={renderItem}
/>
)
};
export default NamazTakibi;
Agenda doesn't update even when new data are added. Only when the app is reloaded it updates.
Here is my code:
const CalendarScreen = () => {
const list = useSelector((state) => state.getTodo.list);
const [items, setItems] = useState({});
const loadItems = () => {
list.forEach((data) => {
const strTime = data.createdDate;
if (!items[strTime]) {
items[strTime] = [];
list.forEach((datalist) => {
items[strTime].push({
name: datalist.title,
});
});
}
});
const newItems = {};
Object.keys(items).forEach((key) => {
newItems[key] = items[key];
});
setItems(newItems);
};
const renderItem = (item) => {
return (
<View >
<Text>{item.name}</Text>
</View>
);
};
return (
<View style={flex: 1}>
<Agenda
items={items}
loadItemsForMonth={loadItems}
renderItem={renderItem}
pastScrollRange={1}
futureScrollRange={1}
/>
</View>
);
};
export { CalendarScreen };
Expectation: Just want the Agenda to update automatically when new data is added in the state instead of having to reload the app.
It looks like that refresh depends call of loadItemsForMonth.
Unfortunately I cannot see when Agenda call loadItemsForMonth
I am new to React (and still new to JS too), and i am trying to build my first React project. I am fetching an API , rendering some items, and building a Search Bar that filters out the items rendered.
My filtering function is more or less working, and inside of it, i store the filtered results in let result , but How i should access those results from the return part (JSX area, i think) to loop over them?
This is my code :
import React, { useState, useEffect } from "react";
import ListItem from "./ListItem";
const List = () => {
const [data, setData] = useState();
const [input, setInput] = useState("");
const onInputChange = (event) => {
setInput(event.target.value);
const value = event.target.value.toLowerCase();
let result = [];
result = data.filter((item) =>
item.name.toLowerCase().includes(value.toLowerCase())
);
setInput(result);
};
useEffect(() => {
const getData = async () => {
const response = await fetch(
"https://rickandmortyapi.com/api/character/"
);
const obj = await response.json();
setData(obj.results);
};
getData();
}, []);
return (
<div>
<input type="text" name={input} onChange={onInputChange}></input>
{data &&
data.map((item) => {
return <ListItem key={item.id} character={item} />;
})}
</div>
);
};
export default List;
So far, I can only loop over input which contains the results, like this input && input.map((item) , but that gives me an empty array when the page is loaded , until i make a search.
You just initialise input as a string so just keep input for keeping input value not result data. You can create another state for keeping result OR put result data back on Data variable.
Here I am showing you to keep result data separate.
import React, { useState, useEffect } from "react";
import ListItem from "./ListItem";
const List = () => {
const [data, setData] = useState();
const [searchResult, setSearchResult] = useState();
const [input, setInput] = useState("");
const onInputChange = (event) => {
setInput(event.target.value);
const value = event.target.value.toLowerCase();
let result = [];
result = data.filter((item) =>
item.name.toLowerCase().includes(value.toLowerCase())
);
setSearchResult(result);
};
useEffect(() => {
const getData = async () => {
const response = await fetch(
"https://rickandmortyapi.com/api/character/"
);
const obj = await response.json();
setData(obj.results);
};
getData();
}, []);
return (
<div>
<input type="text" name={input} onChange={onInputChange}></input>
{input===""? data &&
data.map((item) => {
return <ListItem key={item.id} character={item} />;
}):
searchResult &&
searchResult.map((item) => {
return <ListItem key={item.id} character={item} />;
})
}
</div>
);
};
export default List;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This is separating your original data and search result different.
You need to use a variable to store data after filter:
const [data, setData] = useState([]);
const onInputChange = (event) => {
setInput(event.target.value);
};
const result = data.filter((item) =>
item.name.toLowerCase().includes(input.toLowerCase())
);
return (
...
{result?.map((item) => {
<ListItem key={item.id} character={item} />;
})}
...
)
One possible solution would be to filter while rendering,
In this scenario you would only need to save the the input value (onInputChange):
const onInputChange = (event) => {
setInput(event.target.value);
};
Then while rendering you would need to add the filtering logic:
{ // if input is not empty
data
.filter(item => item.name.includes(input.toLowerCase()))
.map((item) => {
return <ListItem key={item.id} character={item} />;
})