how to fix useEffect has a missing dependency "Dispatch" - reactjs

Hello I have a problem with my eslint and I'm not sure how to respond with my dispatch's:
const { messages } = ChatReducer;
const [isTyping, setIsTyping] = useState(false);
console.log(isTyping)
useEffect(() => {
if (messages[messages.length - 1].type === 'bot') {
setIsTyping(true);
const timeoutId = setTimeout(() => {
setIsTyping(false);
dispatch(wait_end());
}, 3000);
}
}, [messages]);
useEffect(() =>
dispatch(answer_Message(['hi 😃','hi two'])
), []);
return (
<>
{messages.map((message, index) => (
<>
{message.text ? (
<Styled.MessageFlexColumn ref={messagesEndRef} key={index}>
{message.type === 'user' ? (
<UserText key={index}>{message.text}</UserText>
) : (
<BotText
key={index}
isTyping={isTyping && index === messages.length - 1}
>
{message.text}
</BotText>
)}
<Styled.Status />
</Styled.MessageFlexColumn>
) : (
''
)}
<div ref={messagesEndRef} />
</>
))}
</>
)
action:
export const checkMessage = text => {
return dispatch => {
dispatch(sendMessage(text));
dispatch(wait_anwser());
dispatch(botMessage(verify(text)));
};
};
export const answer_Message = text => {
return dispatch => {
text.map((message, index) => {
dispatch(botMessage(message));
})
}
}
Well I also have a problem with my useEffect and my state
I'm basically using it to send an array of initial messages to display in my chat:
useEffect(() =>
dispatch(answer_Message(['hi 😃','hi two'])
), []);
The problem is that when sending 2 texts
and my isTyping state is true only once
even being 2 texts and not enter on my if:
if (messages[messages.length - 1].type === 'bot'
alert eslint:
Line 70:6: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array
Line 74:6: React Hook useEffect has a missing dependency: 'dispatch'. Either include it or remove the dependency array

I don't think you need to dispatch that as your function dispatches as well, or you could try adding the dispatch as a dependency inside the [dispatch]

Related

useEffect Fetch showing 0 array in react

I am creating a React app that uses Fetch to pull a API using SQLite, however for some reason it shows in console length: 3, and Array[0] only. I cannot pull from data just id 1 for example.
import React, { useState, useEffect } from "react";
export default () => {
const [brands, setBrands] = useState(null);
useEffect(() => {
fetch("/api/brands")
.then((response) => response.json())
.then((data) => console.log(data))
.then((data) => {
setBrands(data);
});
}, []);
return (
<>
{brands ? (
<>
<h1>Brands</h1>
<ul>
{brands.map((brand) => (
<li key={brand.id}>{brand.name}</li>
))}
</ul>
</>
) : (
<div>Loading...</div>
)}
</>
);
};
How would I be able to pull from this id 1 for example? at the moment only all the brands show when I remove the console log and show as I pasted in the screen shot above in console.
If I understand your question then you want to display only one item from an array of items fetched via an API.
Its not clear where or how you determine which item you want. Is it a component prop?
My first suggestion is simply implement an API endpoint that returns only one item based on a parameter eg.
fetch(`/api/brands/${some_id_for_the_record_i_want}`)
If you can't modify the API then you can use filter/find to limit the items you want BEFORE setting state eg. - this example uses find which returns a single match or undefined.
useEffect(() => {
fetch("/api/brands")
.then((response) => response.json())
.then((data) => {
setBrands(data.find(f => f.id === 1));
});
}, []);
Worst case just do filter/find in the JSX eg. - this example uses filter which returns an array of matches or an empty array.
return (
<>
{(() => {
if(brands) {
const filteredBrands = brands.filter(f => f.name === 'somename');
return (
<>
<h1>Brands</h1>
<ul>
{filteredBrands.map((brand) => (
<li key={brand.id}>{brand.name}</li>
))}
</ul>
</>
)
// Want a single match?
// const singleBrand = brands.find(f => f.id=== 1);
//return (
// <>
// <h1>Brands</h1>
// <div>{singleBrand?.name}<div>
// </>
//)
} else {
return <div>Loading...</div>
}
})()}
</>
);
From what I understand, you want to show data for a single id (i.e. brand) instead of for all brands. I would do it like this.
import React, { useState, useEffect } from "react";
export default () => {
const [allBrands, setAllBrands] = useState(null);
const [specificBrand, setSpecificBrand] = useState(null);
useEffect(() => {
fetch("/api/brands")
.then((response) => response.json())
.then((data) => {
setAllBrands(data);
});
}, []);
useEffect(() => {
if(allBrands.length){
setSpecificBrand(allBrands.find(brand => brand .id === 1);
}
}, [allBrands]);
return (
<>
{specificBrand? (
<>
<h1>Brands</h1>
<ul>
<li key={specificBrand.id}>{specificBrand.name}</li>
</ul>
</>
) : (
<div>Loading...</div>
)}
</>
);
};
The API endpoint suggestion also seems like a good idea.

How to wait for setState in useEffect until render?

let [item, setItem] = useState({});
let [comments, setComments] = useState([]);
useEffect(async () => {
await axios
.all([
axios.get(`https://dummyapi.io/data/v1/post/${id}`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
}),
axios.get(`https://dummyapi.io/data/v1/post/${id}/comment`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
})
])
.then(
axios.spread((detail, comment) => {
setItem({ ...detail.data })
setComments([...comment.data.data])
})
)
.catch((detail_err, comment_err) => {
console.error(detail_err);
console.error(comment_err);
});
}, []);
i setStated like above.
and I was trying to use the State in return(), but it seems it didn't wait for the data set.
return (
<div>
{item.tags.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
because i got an error message like this : Uncaught TypeError: Cannot read properties of undefined (reading 'map').
Since i initialized 'item' just empty {object}, so it can't read 'item.tags', which is set by setState in useEffect.
How can i wait for the data set?
In generic, it would set a state isFetched to determine if the data from api is ready or not. And when the isFetched equal to true, it means the item.tags have value.
const [isFetched, setIsFetched] = useState(false);
useEffect(async () => {
await axios.all(...).then(() => {
...
...
setIsFetched(true);
})
}, [])
// You could return null or an Loader component meaning the api is not ready
if (!isFetched) return null;
return (
<div>
{item.tags.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
On the other hand, you could use optional chaining to avoid using map from an undefined value (that is item.tags), the right way is replace item.tags.map to item.tags?.map.
Initially, item is an empty JSON ({}). You should be using the optional chaining operator(?.) to easily get rid of the null or undefined exceptions.
return (
<div>
{item?.tags?.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
let [item, setItem] = useState({});
Your initial state is an empty object, and there will always be at least one render that uses this initial state. Your code thus needs to be able to work correctly when it has this state. For example, you could check if item.tags exists before you try to use it:
if (item.tags) {
return (
<div>
{item.tags.map((tag, index) => {
return <Chip label={tag} key={index] />
})}
</div>
);
} else {
return <div>Loading...</div>
}
Alternatively, you could change your initial state so it has the same shape that it will have once loading has finished:
let [item, setItem] = useState({ tags: [] });

How to avoid Can't perform a React state update error when i use ternary operator

I got an error :
index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
And I have been trying to find what makes that error and I found the thing that makes an error.
so I tried to search how to avoid this error in this case. but I couldn't find it.
so The problem is when I upload the csv file and then the file contains info state.
so I show this file information on my website.
And when the file is uploaded then the component is changing
So I used it with the ternary operator. So I tried to remove the ternary operator then the error had disappeared I assumed that it made the error .
So I'm trying to fix it but I can't figure it out
here is my code :
const CsvShowData = ({ info, setInfo }) => {
return (
//
<>
{info.length !== 0 ? (
<DataTable>
{info.slice(0, 1).map(inf => (
<MainRow key={inf}>
{inf.map((d, index) => (
<Row key={index}>
<div className="titleRow">
<h3>{d}</h3>
</div>
</Row>
))}
</MainRow>
))}
{info.slice(1, 10).map((a, key) => (
<MainRow key={key}>
{a.map((b, idx) => (
<Row key={idx}>
<div className="sideRow">
<p>{b}</p>
</div>
</Row>
))}
</MainRow>
))}
</DataTable>
) : (
<CsvTable>
<CsvFileReader info={info} setInfo={setInfo} />
</CsvTable>
)}
</>
);
};
Thank you in advance!
CsvFileReader Component
const CsvFileReader = ({ setInfo }) => {
const handleOnDrop = data => {
const infos = data.map(item => item.data);
setTimeout(() => setInfo([...infos]), 1000); // save timeout ref
};
const handleOnError = (err, file, inputElem, reason) => {
console.log(err);
};
const handleOnRemoveFile = data => {
console.log(data);
};
return (
<>
<MainReader>
<CSVReader
onDrop={handleOnDrop}
onError={handleOnError}
config={
(({ fastMode: true }, { chunk: "LocalChunkSize" }),
{ header: false })
}
addRemoveButton
onRemoveFile={handleOnRemoveFile}
>
You should use a ref to save setTimeout and remove setInfo when component is unmounted.
const ref = useRef();
const handleOnDrop = (data) => {
const infos = data.map((item) => item.data);
ref.current = setTimeout(() => setInfo([...infos]), 1000); // save timeout ref
};
useEffect(() => {
return () => {
if (ref.current) {
clearTimeout(ref.current);
}
};
});

React flashes message before state is set in useEffect

I am having an issue where the null message (I dont have Partners) is flashing up for a second or so before the state is set and the message changes .. Is there any way to not show it until everything is resolved?
const Dashboard = () => {
const [partners, setPartners] = useState([]);
useEffect(() => {
async function getPartners() {
await axios.get('/api/partners').then(response => {
setPartners(response.data.data);
})
}
getPartners();
}, []);
return (
<>
{partners?.length ? <div><p>I have Partners</p></div> : <div><p>I dont have Partners</p></div>}
</>
)
}
export default Dashboard;
There are several approaches to achieve such a thing the simplest one is to use another state to indicate the HTTP request status. Here how it is can be implemented.
const Dashboard = () => {
const [partners, setPartners] = useState([]);
const [status, setStatus] = useState('idle');
useEffect(() => {
setStatus('pending');
async function getPartners() {
await axios.get('/api/partners').then(response => {
setPartners(response.data.data);
setStatus('resolved');
})
}
getPartners();
}, []);
return (
<>
{status === 'idle' || status === 'pending' ?
<LoadingComponent /> // A custom component to represent loading status
:
<>
{partners?.length ? <div><p>I have Partners</p></div> : <div><p>I dont have Partners</p></div>}
<>
}
</>
)
}
export default Dashboard;

Need useEffect hook invoke when URL changes

I am trying to have this component load data depending on its current url whether /global or /my-posts. The useEffect() grabs the data from the first loading of the component but when i change to another url i expected useEffect to check the url again and load the correct data but instead i'm stuck with the data from the first load. How do i get useEffect to invoke every time i click between different urls like /global and /my-posts url.
export default function Dashboard() {
const [allRecipes, setAllRecipes] = useState([]);
const [myRecipes, setMyRecipes] = useState([]);
const currentUrl = window.location.pathname;
useEffect(() => {
if (currentUrl === '/dashboard/global') {
console.log('hello');
trackPromise(
RecipeService.getAllRecipes()
.then((data) => {
setAllRecipes(data);
}),
);
} else if (currentUrl === '/dashboard/my-posts') {
console.log('hi');
trackPromise(
RecipeService.getRecipes()
.then((data) => {
setMyRecipes(data);
}),
);
}
}, []);
console.log(window.location.pathname);
return (
<>
<div className="dashboard">
<DashboardHeader />
<div className="created-posts">
{allRecipes.length !== 0
? allRecipes.map((recipe) => <Post recipe={recipe} key={uuidv1()} />)
: null}
{myRecipes.length !== 0
? myRecipes.recipes.map((recipe) => <Post recipe={recipe} key={uuidv1()} />)
: null}
{currentUrl === '/dashboard/create' ? <CreateForm /> : null}
<LoadingIndicator />
</div>
</div>
</>
);
}
to make React.useEffect run on every currentUrl change, you have to add it to useEffect dependencies array.
// first we need to control the state of window.location.pathname by react not the browser
// and make react state be the only source of truth.
const pathname = window.location.pathname
// manage currentUrl in state.
const [currentUrl, setCurrentUrl] = React.useState(pathname)
React.useEffect(() => {
setCurrentUrl(pathname)
}, [pathname])
// now you would add the contolled `currentUrl` state to its useEffect deps.
useEffect(() => {
if (currentUrl === '/dashboard/global') {
// ..........
} else if (currentUrl === '/dashboard/my-posts') {
// ..........
}
}, [currentUrl]);

Resources