React state is empty even it does have a value - reactjs

const CourseTags = () => {
const [tags, setTags] = useState([])
useEffect(() => {
const jwt = sessionStorage.getItem("jwt")
// GET tags
fetch(`${process.env.NEXT_PUBLIC_HOST}/api/tags`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${jwt}`
}
})
.then(res => res.json())
.then(data => {
setTags(data)
console.log(data) // <<<<<<< shows Array of tags [ {}, {}, ... ]
})
.catch(err => {
console.log(err)
})
}, [])
const deleteTag = (tagId) => {
if (confirm("Delete Tag?")) {
console.log(tags, tagId) // <<<<<<<<< tags = [ ]
}
}
return (
<Fragment>
<TagsList tagsList={tags} handleDeleteTag={(tagId) => deleteTag(tagId)} />
</Fragment>
)
}
When invoking deleteTag function .. it shows tagsList as [ ]
// console.log(tags, tagId) shows -->
// [] '63d65592bac78b25feb8f482'
*** knowing that I am calling deleteTag(tagId) from another sub-component ***

I've missed the dependency array [tagsList] inside my sub-component's useMemo()
once added it resolved the issue
const TagsList = ({tagsList, handleDeleteTag}) => {
const columns = useMemo(() => [{}, {}, ...] , [tagsList]) // <<<< [tagsList] was missed
.....
}
thanks for help,

Related

Return of Child Component within useEffect and Fetch not working

I'm facing a weird problem. The next code returns correctly inside its useEffect and fetch functions... Nevertheless, the child component is not being rendered. I think it might be because of the map(), but i'm not even sure about it.
Any ideas? Thanks in advance.
const FilesComponent = () => {
let result = useRef({
files: [],
folders: []
});
useEffect(() => {
console.log('useEffect...');
fetch('http://192.168.1.36:3001/api/files/', {
method: 'GET',
headers: {
'Content-Type': 'application/json; charset=',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYmxhY2tvdXQiLCJlbWFpbCI6ImJsYWNrb3V0QGRvbWFpbi5jb20iLCJpYXQiOjE2NDkxMjk4OTMsImV4cCI6MTY0OTIxNjI5MywiaXNzIjoiY2xvdWRsaXRlLXR5cGVzY3JpcHQifQ.i6ik5YTxahBWwKVJdWJUw6dR4vAkwM4ytc-3eSBSzoQ'
},
credentials: 'include'
})
.then(response => response.json())
.then(data => {
dispatch({type: 'ChangeMessage',
files: data.data.files,
folders: data.data.folders
});
const action = {
type: 'ChangeMessage'
};
result.current = reducer({}, action);
console.log(result.current);
return (
<React.Fragment>
<ul className="files-list">
{
result.current.files.map(file => <File href={'https://www.google.com/'} key={file.name} value={file}/>)
}
{
result.current.folders.map(folder => <File href={'https://www.google.com/'} key={folder.name} value={folder}/>)
}
</ul>
</React.Fragment>
);
});
}, [result]);
return (
<>
</>
);
};

useEffect: How to put data in the state in order

I'd like to ask how to retrieve data through use Effect.
The flow I want is as follows.
First, I want to get the 'cards' state, fill the cards with data, and then fill the data through the cardsPromises after that.
But my code couldn't get cards and wordAll, and the empty value came out.
I think it's because the cards are still empty, but I don't know how to operate in order.
Please tell me how to do it.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
axios
.get("http/api/words/", {
headers: {
Authorization: cookies.token,
},
})
.then((response) => {
setCards(response.data);
})
.catch((error) => {
console.log(error);
});
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
)
);
console.log("cards", cards);
Promise.all(cardsPromises)
.then((response) => {
console.log("resp", response.data);
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
}, []);
You are correct, cards array is still empty in the useEffect callback when the fetching the data. I suggest converting to async/await and waiting for the first fetch to resolve and using that value of cards for the fetching of the rest of the data.
const [wordAll, setWordAll] = useState([]);
const [cards, setCards] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const{ data: cards } = await axios.get(
"http/api/words/",
{
headers: {
Authorization: cookies.token,
},
},
);
setCards(cards);
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {
Authorization: cookies.token,
},
}
);
);
const wordAllResponse = await Promise.all(cardsPromises);
const wordAll = wordAllResponse.map(({ data }) => data);
setWordAll(wordAll);
} catch (error) {
// handle any errors, rejected Promises, etc..
}
};
fetchData();
}, []);
Wrap your 2nd axios call inside a function, and call it after 1st axios call returns.
useEffect(() => {
const getWords = (cards) => {
const cardsPromises = cards.map((contents) =>
axios.get(
`http/api/words/detail_list/?contents=${contents.contents}`,
{
headers: {Authorization: cookies.token}
}
)
);
Promise.all(cardsPromises)
.then((response) => {
setWordAll(response.data);
})
.catch((error) => {
console.log("err==>", error);
});
})
axios
.get("http/api/words/", {
headers: { Authorization: cookies.token },
})
.then((response) => {
const cards = response.data;
setCards(cards);
getWords(cards);
})
.catch((error) => {
console.log(error);
});
}, [])
Now dependency chain is clearer.

React renders component many times when fetching data

I'm using fetch api so my goal is to fire a POST request and then store the result received from this call in a state. This is the code that I use:
interface IPreviewFile {
file: IFile;
}
export default function PreviewFileModal({ file }: IPreviewFile) {
const source = useSelector((state: AppState) => state.source);
const project = useSelector((state: AppState) => state.project);
const data = {
fileName: file.path,
accountName: source.Name,
bucket: source.bucket,
id: source.id
};
useEffect(() => {
Promise.all([
fetch(`${API_URL}/api/project/${project.id}/filepreview`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
])
.then(async ([aa]) => {
const a = await aa.json();
return [a];
})
.then((responseText) => {
setStringArray(readString(responseText[0].value).data);
})
.catch((err) => {
console.log(err);
});
}, [project.id, data]);
console.log(stringArray);
return (
<>
<div></div>
</>
);
}
The console.log(stringArray); prints in the console all the time after delay of 2-3 seconds. As you can see, I use Promise in order to avoid this but for some reason it still happens. Any ideas what causes the re-rendering all the time and how to fix it?
I've tried changing the code a bit to avoid re-rendering the component due to the data variable added as a dependency to useEffect. I don't see any reference to stringArray, so i've added it as a state variable.
export default function PreviewFileModal({ file }: IPreviewFile) {
const source = useSelector((state: AppState) => state.source);
const project = useSelector((state: AppState) => state.project);
const [stringArray, setStringArray] = useState("");
useEffect(() => {
fetch(`${API_URL}/api/project/${project.id}/filepreview`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
fileName: file.path,
accountName: source.Name,
bucket: source.bucket,
id: source.id
})
})
.then(res => res.json())
.then((result) => {
result && setStringArray(readString(result.value).data);
})
.catch((err) => {
console.log(err);
});
}, [project.id]);
console.log(stringArray);
return (
<>
<div></div>
</>
);
}

how to use aync and await inside use effect of calling function?

I am calling following functions like below.
How I can give async await to the serviceRequest and success handler
useEffect(() => {
serviceRequest(
"URL",
success,
error
);
}, []);
const success = (response) => { }
const error = (error) => { }
export const serviceRequest = (endpoint,successCallBack,errorCallBack) => {
const options: any = {
withCredentials: true,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
axios.get(endpoint, options)
.then((response) => {
successCallBack(response.data)
})
.catch(error => {errorCallBack(error) })
}
Well, first make sure that the serviceRequest function returns a promise. In this case you could simply return the axios result:
export const serviceRequest = (endpoint,successCallBack,errorCallBack) => {
const options: any = {
withCredentials: true,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
return axios.get(endpoint, options)
}
Then you can use the promise in your use-effect handler, catching the result / error in a useState hook like this:
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
serviceRequest("URL")
.then((result) => setState(result)
.catch(error => setState(error)
};
}, []);
You can decalre an async function in the useEffect and call it:
useEffect(() => {
const callApi = async () => {
await serviceRequest("URL", success, error);
};
callApi();
}, []);
You could write something like:
useEffect(() => {
(async () => {
await serviceRequest("URL", success, error);
})()
}, []);

How to set fetch data to text field in react-native function component

I am learning react-native and have a question about fetching data and passing them to a text component.
I fetched my data from my node.js back-end but don't know how to pass this data to component. Below is the code that i have tried so far.
const TableView = () => {
const [details, setDetails] = useState('');
const getUserData = async () => {
fetch('https://----.herokuapp.com/getIncomePerUser', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: data,
month: value,
}),
})
.then(response => response.json())
.then(response => {
console.log('Test');
console.log(response);
const array = response;
for (const i of array) {
const total = i.total;
setDetails(total);
console.log(total);
}
})
.catch(err => {
console.log(err);
});
});
};
useEffect(() => {
getUserData();
}, []);
return (
<Text Value={details}></Text> //I need to set my fetch data this text component
)
}
if you have an array of values and you want to show them you can use:
const TableView = () => {
const [details, setDetails] = useState('');
const getUserData = async () => {
fetch('https://----.herokuapp.com/getIncomePerUser', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: data,
month: value,
}),
})
.then(response => response.json())
.then(response => {
setDetails(response.map(r => r.total));
})
.catch(err => {
console.log(err);
});
});
};
useEffect(() => {
getUserData();
}, []);
return (<>
{details.map((d, i) => <Text key={i}>{d}</Text>)}
</>)
}
if you have a single value just replace your text component with:
<Text>{details}</Text>

Resources