How can I get the data back with useState? - reactjs

const noticeList = useSelector(state => state.noticeReducer.list) //현재 페이지에 띄워질 공지 리스트
//page
const [current, setCurrent] = useState(0); //현재 페이지
const pageInfo = useSelector(state => state.noticeReducer.pageInfo); //전체 페이지 정보
const [keyword, setkeyword] = useState(null); //키워드 state
const [searchedList, setsearchedList] = useState(noticeList); // 검색 할때만 사용하므로 여기에 사용
const [active, setactive] = useState("");
console.log(searchedList)
const Search = () => {
const data = axios.post('/noticeList',{
keyword : keyword,
})
.then(res => res.data)
.catch(err => console.log(err));
setsearchedList(data)
}
useEffect(() => {
return (
dispatch(getNoticeList(current+1,keyword)) //공지사항 목록 받아오기
)
}, [dispatch, current])
//화면에 출력하기 위해 map 함수를 활용
let homeNotice = searchedList.map(
item =>
{
return(
<NoticeDetail key = {item.noticeId} title = {item.title} active = {active} setactive = {setactive} content = {item.content}/>
)
}
)
I saved the data in Redux in the state with useEffect.
I want to overwrite the data in the same state when searching in the search function. What should I do?
Uncaught TypeError: searchedList.map is not a function

You are not using promises correctly you should set the data with the result of the promise, not with the promise:
const Search = () => {
axios
.post('/noticeList', {
keyword: keyword,
})
.then(({ data }) => setsearchedList(data))
.catch((err) => console.log(err));
};
I am assuming the api call resolves with data being an array.

Related

Map data on runtime after post request

I have three apis in all. GetAssets is the first, followed by assetsOptionsList and getAssetsLibrary. The issue I'm having is that when I post the data on getAssetsLibrary, I want to be able to present it on get Assets at runtime.Everything is working fine but i want to show assets on runtime.
I'm setting the runTime state true on get request but the problem is it works only for one time.Second time, it does not map on runtime. Actually, i want to know is there any alternative so that i can achieve the goal.
In the below code the one function is getting the assets. And i want to run the one function when the post request successfully sent.
const [images, setImages] = useState([]);
const [assetOptions, setAssetOptions] = useState([]);
const [faqOpened, setToggleFaq] = useState(false);
const [runTime, setRunTime] = useState(false)
const [assetID, setAssetID] = useState()
const [isLoading, setIsLoading] = useState(false);
const handleForm = (e) => {
const index = e.target.selectedIndex;
const el = e.target.childNodes[index]
const option = el.getAttribute('id');
setAssetID(option)
}
const formHandler = (e) => {
e.preventDefault()
let formData = new FormData();
formData.append('media', e.target.media.files[0]);
formData.append('assetListId', assetID)
formData.append('name', e.target.name.value);
console.log(Object.fromEntries(formData))
const res = axios.post('api/asset-library',
formData
).then((response) => {
showSuccessToaster(response?.data?.message)
setRunTime(true)
setToggleFaq(false)
})
.catch((error) => {
showErrorToaster(error?.response?.data?.message)
})
}
const showSuccessToaster = (response) => {
return uploadToasterSuccess.show({ message: response });
}
const showErrorToaster = (error) => {
return uploadToasterError.show({ message: error });
}
const one = async () => {
setIsLoading(true)
const data = await axios.get('api/assets').then((res) => {
return res?.data?.data
})
setImages(data)
setIsLoading(false)
}
const two = async () => {
const data = await axios.get('/api/asset-list').then((res) => {
return res?.data?.data
})
setAssetOptions(data)
}
useEffect(() => {
one()
two()
}, [runTime]);

How to combine multiple API requests in one function with fetch() in React

I have to functions getDataOne and getDataTwo. How do I combine below into one function, using fetch(), useState and useEffect?
const MyComponent = () => {
const [loading, setLoading] = useState(false);
const [dataOne, setDataOne] = useState<Data[]>([]);
const [dataTwo, setDataTwo] = useState<Data[]>([]);
const getDataOne = async () => {
setLoading(true);
const result = await fetch(
"https://my-api-link-one"
);
const jsonResult = await result.json();
setLoading(false);
setDataOne(jsonResult);
};
const getDataTwo = async () => {
setLoading(true);
const result = await fetch(
"https://my-api-link-two"
);
const jsonResult = await result.json();
setLoading(false);
setDataTwo(jsonResult);
};
useEffect(() => {
getDataOne();
getDataTwo();
}, []);
Update:
I set it up using Promise.all
const [loading, setLoading] = useState(false);
const [dataOne, setDataOne] = useState<DataOne[]>([]);
const [dataTwo, setDataTwo] = useState<DataTwo[]>([]);
const [data, setData] = useState<DataOne[] & DataTwo>([]);
const urls = [
"https://url-one", "https://url-two",
];
const getData = async () => {
setLoading(true);
const results = await Promise.all(
urls.map((url) => fetch(url).then((res) => res.json()))
);
setLoading(false);
setData(results);
console.log(data);
};
This is not totally working yet. How do I use useState now correctly (and handle both data from urls)? In the end I want to have one data variable so I can map over this variable:
{data.map((item) => {
return (
// etc
So, Promise.all() accepts an array of promises, so naturally Promise.all() returns an array only. So even though your results variable still is an array I would recommend destructuring it because in this case there are only two API fetches involved. Looking at your update, I think there's only small modifications left which are as follows :
const urls = ["https://url-one", "https://url-two",];
const getData = async () => {
setLoading(true);
const [result1, result2] = await Promise.all(
urls.map((url) => fetch(url).then((res) => res.json()))
);
setLoading(false);
setDataOne(result1);
setDataTwo(result2);
console.log(data);
};
You can use Promise.all. Read more here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all.
const getData = () => {
setLoading(true);
Promise.all([fetch('api-1'), fetch('api-2')]).then(results => {
setDataOne(results[0]);
setDataTwo(results[1]);
}).finally(() => setLoading(false));
}
Utilize .flat() to reformat the data array returned from the Promise.all() into your state which holds the response obj/array,
Promise.all(
urls.map(url =>
fetch(url).then(e => e.json())
)
).then(data => {
finalResultState = data.flat();
});

How to get the current state inside socket.io on callback function

const useChat = () => {
const [messages, setMessages] = useState([]);
const socketRef = useRef();
const { chatId } = useSelector(state => state.chatin)
const { chatList } = useSelector(state => state.chatin)
const dispatch = useDispatch()
useEffect(() => {
socketRef.current = io(socketClient);
socketClient.on('chat', (data) => {
const targetMessage = (messages) => messages.findIndex(item => item.message_number === data.message_number);
console.log('targetMessage', targetMessage)
if (targetMessage !== -1) {
messages[targetMessage].is_hide = true;
}
setMessages((messages) => [...messages, data]);
});
return () => {
socketRef.current.disconnect();
};
}, []);
whenever I got new socket data, I wanna change 'messages' data, but can't access it, because it always shows initial data value.After that I have a question about how can I set it?
You can move the if condition inside setMessages function, this way you will get access to the current state:
socketClient.on('chat', (data) => {
setMessages((messages) => {
const targetMessage = messages.findIndex(item => item.message_number === data.message_number);
if (targetMessage !== -1) {
messages[targetMessage].is_hide = true;
}
return [...messages, data]
});
});

useEffect and async()

In the useEffect() hook, I am basically trying to add an 'id' to each 'item'object mapped to tempData, by incrementing the lastIndex state in each iteration. However, all the item.id s that have been mapped returned 0 (the initial state value).
I am guessing there is something wrong with invoking the setLastIndext function in the iterations? Thanks.
const SearchAppointments = React.memo(() => {
const [data, setData] = useState([ ])
const [lastIndex, setLastIndex] = useState(0)
useEffect( () => {
const fetchData = async() => {
var response = await fetch('../data.json');
var result = await response.json()
var tempData = result.map( item => {
item.id = lastIndex;
setLastIndex(lastIndex => lastIndex + 1);
return item
})
setData(tempData)
};
fetchData();
}, [])
return (
<div>
</div>
);
})
setLastIndex is async function the value of lastIndex will only be updated in next render, but result.map is sync function ==> lastIndex always 0 in result.map
You can try this:
const SearchAppointments = React.memo(() => {
const [data, setData] = useState([ ])
// You not really need this lastIndex state for setting id for your data item, but somehow you want it after setData you can keep this and set to the last index of the item in fetched data
const [lastIndex, setLastIndex] = useState(0)
useEffect( () => {
const fetchData = async() => {
var response = await fetch('../data.json');
var result = await response.json()
var tempData = result.map( (item, index) => ({...item, id: index}))
setLastIndex(tempData.length -1)
setData(tempData)
};
fetchData();
}, [])
return (
<div>
</div>
);
})
You will try this:
const SearchAppointments = React.memo(() => {
const [data, setData] = useState([]);
const fetchData = async() => {
const response = await fetch('../data.json');
const result = await response.json();
setData(result.map( (item, index) => ({...item, id:index})))
};
useEffect( () => {
fetchData();
}, [])
return (
<div>
</div>
);
})```
Do you really need lastIndex?
There's data.length.
It can be used inside setData(lastData=>...)

React hooks - fetching data from api and passing to a component

So basically, I'm trying to fetch data from api and pass it to Component.
I create usePosition hook to get my positon from browser, and then get response from api. I really don't know how to wait with useEffect for my position, when i'm executing this code now I'm getting always log 'no position'.
const usePosition = () => {
const [error, setError] = useState(null);
const [position, setPosition] = useState();
useEffect(() => {
const geo = navigator.geolocation;
if(!geo) {
setError('Geolocation is not supported.');
return;
}
const handleSuccess = position => {
const { latitude, longitude } = position.coords;
setPosition({
latitude,
longitude
});
};
const handleError = error => {
setError(error.message);
};
geo.getCurrentPosition(handleSuccess, handleError);
}, []);
return { position, error };
}
function App() {
const {position, error} = usePositon();
const [weather, setWeather] = useState([]);
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, []);
return (
<div className="App">
<div>
<Swiper weather={weather}/>
</div>
</div>
)
}
It's all because of [] empty dependencies list down in App's useEffect. It runs exactly once on mount, when usePosition has not requested anything yet. And once it successes later and returns different { error, position } App does not react.
How to solve? Provide things as dependencies:
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, [position, error]);

Resources