React renders component many times when fetching data - reactjs

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>
</>
);
}

Related

React state is empty even it does have a value

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,

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.

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>

Fetch request is not updating the state

I have an react application connected to a database. Currently the app takes the database when it mounts and uses that to populate the state. The apps allows someone to post to the database. So far that works.
The issue is that I want the new posted content to be seen by the user. As it is the content only populates after I reload the page. I tried to repeat the coding in the componentDidMount() in a function that runs after the POST request, but for someone reason that is not working.
class App extends Component {
state = {
notes: [],
folders: [],
//noteID: 0,
//folderID: 0
};
componentDidMount() {
Promise.all([
fetch(`${config.API_ENDPOINT}/notes`),
fetch(`${config.API_ENDPOINT}/folders`)
])
.then(([notesRes, foldersRes]) => {
if (!notesRes.ok)
return notesRes.json().then(e => Promise.reject(e))
if (!foldersRes.ok)
return foldersRes.json().then(e => Promise.reject(e))
return Promise.all([
notesRes.json(),
foldersRes.json(),
])
})
.then(([notes, folders]) => {
this.setState({ notes, folders })
})
.catch(error => {
console.error({ error })
})
}
pageReload = () =>{
//console.log('pageReload ran');
Promise.all([
fetch(`${config.API_ENDPOINT}/notes`),
fetch(`${config.API_ENDPOINT}/folders`)
])
.then(([notesRes, foldersRes]) => {
if (!notesRes.ok)
return notesRes.json().then(e => Promise.reject(e))
if (!foldersRes.ok)
return foldersRes.json().then(e => Promise.reject(e))
return Promise.all([
notesRes.json(),
foldersRes.json(),
])
})
.then(([notes, folders]) => {
this.setState({ notes, folders })
})
.catch(error => {
console.error({ error })
})
}
folderSubmit = (f) => {
//console.log("folderSubmit ran " + f);
const newFolder = { "name" : f };
const postfolder = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(newFolder)
};
fetch(`${config.API_ENDPOINT}/folders`, postfolder).then(this.pageReload())
}
It looks like you are not setting your state after post. see below where you need to set your state.
folderSubmit = (f) => {
//console.log("folderSubmit ran " + f);
const newFolder = { "name" : f };
const postfolder = {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify(newFolder)
};
fetch(`${config.API_ENDPOINT}/folders`, postfolder)
.then(response => response.json())
.then(data => this.setState(data));
}

Resources