Here's my code first
const [getData, setGetData] = useState();
const [ref, setRef] = useState();
const initializeData = async() => {
const userToken = await AsyncStorage.getItem('user_id');
setGetData(JSON.parse(userToken));
}
useEffect(() => {
return initializeData();
},[])
useEffect(() => {
let interval;
if(getData != null)
{
interval = setInterval(() => {
setRef(firestore().collection('**********').where("SendersNo", "==", getData.number));
}, 2000);
}
return () => clearInterval(interval);
},[getData])
useEffect(() => {
if(ref != null)
{
return ref.onSnapshot(querySnapshot => {
const list = [];
querySnapshot.forEach(doc => {
const {
id,driverName,driverContactNumber,driverRating,driverPlateNumber,driverTrackingNumber,userPlaceName,
destinationPlaceName,PaymentMethod,Fare
} = doc.data();
list.push({id: doc.id,driverName,driverContactNumber,driverRating,
driverPlateNumber,driverTrackingNumber,userPlaceName,destinationPlaceName,PaymentMethod,Fare});
});
setUserBookingData(list);
console.log("HEY!");
});
}
},[])
const CurrentTransaction = () => {
if(ref == null)
{
return (
<View>
<Text>You don't have a Current Transaction</Text>
</View>
)
}
else
{
return userBookingData.map((element) => {
return (
<View key={element.id}>
<View>
<Text>{element.name}</Text>
</View>
</View>
)
});
}
}
So currently right now what I am trying to is if there's a data on my firestore it will update on the screen but before updating it I need to get the data from the setGetData so that I can query it but the problem is that when I refresh the whole simulator/page it doesn't get the data but instead just a blank page . But when i edit and save my code without refreshing the page/simulator it can get the data . Can someone help me what I am doing wrong .
EDIT
if I do this
useEffect(() => {
if(ref != null)
{
return ref.onSnapshot(querySnapshot => {
const list = [];
querySnapshot.forEach(doc => {
const {
id,driverName,driverContactNumber,driverRating,driverPlateNumber,driverTrackingNumber,userPlaceName,
destinationPlaceName,PaymentMethod,Fare
} = doc.data();
list.push({id: doc.id,driverName,driverContactNumber,driverRating,
driverPlateNumber,driverTrackingNumber,userPlaceName,destinationPlaceName,PaymentMethod,Fare});
});
setUserBookingData(list);
console.log("HEY!");
});
}
else
{
return null;
}
},[ref])
it keeps looping the console.log('hey') but it can get the data and display it . but it loops so its bad.
i believe snapshot from firebase realtime database is a listener so its doesn't need setinterval
useEffect(() => {
if(getData != null)
{
const ref = firestore().collection('**********').where("SendersNo", "==", getData.number);
ref.onSnapshot(querySnapshot => {
const list = [];
querySnapshot.forEach(doc => {
const {
id,driverName,driverContactNumber,driverRating,driverPlateNumber,driverTrackingNumber,userPlaceName,
destinationPlaceName,PaymentMethod,Fare
} = doc.data();
list.push({id: doc.id,driverName,driverContactNumber,driverRating,
driverPlateNumber,driverTrackingNumber,userPlaceName,destinationPlaceName,PaymentMethod,Fare});
});
setUserBookingData(list);
console.log("HEY!");
});
}
return () => {
//clear your ref listener here
}
},[getData])
if you put a return on use effect it will be called after the screen is no longer used.
useEffect(()=>{
//inside this will be called when the screen complete render
const someListener = DeviceEventEmitter('listentosomething',()=>{
//do something
});
return ()=>{
//inside this will be called after the screen no longer be used
//example go to other screen
someListener.remove();
}
},)
Related
I often use this code to fetch and update data for my like button. It works but I wonder if there is a more effective or cleaner way to do this function.
const isPressed = useRef(false); // check the need to change the like count
const [like, setLike] = useState();
const [count, setCount] = useState(count_like); // already fetch data
const [haveFetch, setHaveFetch] = useState(false); // button block
useEffect(() => {
fetchIsLike(...).then((rs)=>{
setLike(rs);
setHaveFetch(true);
})
return () => {}
}, [])
useEffect(()=>{
if(like) {
// animation
if(isPressed.current) {
setCount(prev => (prev+1));
// add row to database
}
}
else {
// animation
if(isPressed.current) {
setCount(prev => (prev-1));
// delete row from database
}
}
}, [like])
const updateHeart = () => {
isPressed.current = true;
setLike(prev => !prev);
}
First time posting and have my Learner plates on.
Using axios with a json mock-server with gets through a ContextProvider to trying to map my data to present with Bootstrap cards.
(then) also click on the card to present on another page (not attempted yet)
My data was presenting fine in list form and in a card through Outlet - but I'm trying instead to present the whole array through cards.
I'd appreciate any help I can get. Apologies in advance for the lengthy code (I'm not sure where the problem is)
ProductList.js
import React from 'react'
import { ProductContext } from './ProductContext'
import ProductDetail from './ProductDetail'
function ProductList(props) {
function productList(products) {
if (products === null) return
return (
<div className='card-container'>
{
products.map(product => (<ProductDetail />))
}
</div>
)
}
return (
<div direction="horizontal" style={{ textAlign: 'center' }}>
<h1>Artworks</h1>
<ProductContext.Consumer>
{({ products }) => (
productList(products)
)}
</ProductContext.Consumer>
</div>
)
}
export default ProductList
ProductDetail.js
function ProductDetail(props) {
const hasFetchedData = useRef(false)
let params = useParams()
let navigate = useNavigate()
let { getProduct, deleteProduct } = useContext(ProductContext)
let [product, setProduct] = useState()
useEffect(() => {
if (!hasFetchedData.current) {
const res = axios.get("http://localhost:3002/products");
setProduct(res);
hasFetchedData.current = true;
}
}, [])
useEffect(() => {
async function fetch() {
await getProduct(params.productId)
.then((product) => setProduct(product))
}
fetch()
}, [params.productId]); // eslint-disable-line react-hooks/exhaustive-deps
let [error, setError] = useState()
useEffect(() => {
setError(null)
async function fetch() {
await getProduct(params.productId)
.then((product) => setProduct(product))
.catch((message) => setError(message))
}
fetch()
}, [params.productId, getProduct])
function errorMessage() {
return <Alert variant="danger">Stockroom is empty: {error}</Alert>
}
function handleDeleteProduct(id) {
deleteProduct(id)
navigate('/products')
}
function loading() {
return <div className="w-25 text-center"><Spinner animation="border" /></div>
}
function productCard() {
let { id, artistname, born, piecename, painted, imgurl, price } = product
return (
<Card className="w-25" key={product.id}>
<Card.Img variant="top" src={imgurl} />
<Card.Body>
<Card.Title>{artistname} {born}</Card.Title>
<Card.Subtitle className="mb-2 text-muted">{piecename}</Card.Subtitle>
<Card.Subtitle className="mb-2 text-muted">{painted}</Card.Subtitle>
<Card.Text>
<strong>Price:</strong> <span>${price}</span>
</Card.Text>
<Link to={`/products/${id}/edit`} className="btn btn-primary mx-3">Edit</Link>
<Button variant="danger" onClick={handleDeleteProduct.bind(this, id)}>Delete</Button>
</Card.Body>
</Card>
)
}
if (error) return errorMessage()
if (product === undefined) return loading()
return product.id !== parseInt(params.productId) ? loading() : productCard()
}
export default ProductDetail
ProductContext.js
export const ProductContext = createContext()
export const ProductProvider = (props) => {
const [products, setProducts] = useState([])
useEffect(() => {
async function getProducts() {
await refreshProducts()
}
getProducts()
}, []);
function refreshProducts() {
return axios.get("http://localhost:3002/products")
.then(response => {
setProducts(response.data)
})
}
function getProduct(id) {
return axios.get(`http://localhost:3002/products/${id}`)
.then(response =>
new Promise((resolve) => resolve(response.data))
)
.catch((error) =>
new Promise((_, reject) => reject(error.response.statusText))
)
}
function deleteProduct(id) {
axios.delete(`http://localhost:3002/products/${id}`).then(refreshProducts)
}
function addProduct(product) {
return axios.post("http://localhost:3002/products", product)
.then(response => {
refreshProducts()
return new Promise((resolve) => resolve(response.data))
})
}
function updateProduct(product) {
return axios.put(`http://localhost:3002/products/${product.id}`, product)
.then(response => {
refreshProducts()
return new Promise((resolve) => resolve(response.data))
})
}
return (
<ProductContext.Provider
value={{
products,
refreshProducts,
getProduct,
deleteProduct,
addProduct,
updateProduct
}}
>
{props.children}
</ProductContext.Provider>
)
}
Configured my store this way with redux toolkit for sure
const rootReducer = combineReducers({
someReducer,
systemsConfigs
});
const store = return configureStore({
devTools: true,
reducer: rootReducer ,
// middleware: [middleware, logger],
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: false }).concat(middleware),
});
middleware.run(sagaRoot)
And thats my channel i am connecting to it
export function createSocketChannel(
productId: ProductId,
pair: string,
createSocket = () => new WebSocket('wss://somewebsocket')
) {
return eventChannel<SocketEvent>((emitter) => {
const socket_OrderBook = createSocket();
socket_OrderBook.addEventListener('open', () => {
emitter({
type: 'connection-established',
payload: true,
});
socket_OrderBook.send(
`subscribe-asdqwe`
);
});
socket_OrderBook.addEventListener('message', (event) => {
if (event.data?.includes('bids')) {
emitter({
type: 'message',
payload: JSON.parse(event.data),
});
//
}
});
socket_OrderBook.addEventListener('close', (event: any) => {
emitter(new SocketClosedByServer());
});
return () => {
if (socket_OrderBook.readyState === WebSocket.OPEN) {
socket_OrderBook.send(
`unsubscribe-order-book-${pair}`
);
}
if (socket_OrderBook.readyState === WebSocket.OPEN || socket_OrderBook.readyState === WebSocket.CONNECTING) {
socket_OrderBook.close();
}
};
}, buffers.expanding<SocketEvent>());
}
And here's how my saga connecting handlers looks like
export function* handleConnectingSocket(ctx: SagaContext) {
try {
const productId = yield select((state: State) => state.productId);
const requested_pair = yield select((state: State) => state.requested_pair);
if (ctx.socketChannel === null) {
ctx.socketChannel = yield call(createSocketChannel, productId, requested_pair);
}
//
const message: SocketEvent = yield take(ctx.socketChannel!);
if (message.type !== 'connection-established') {
throw new SocketUnexpectedResponseError();
}
yield put(connectedSocket());
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.BAD_CONNECTION,
})
);
}
}
export function* handleConnectedSocket(ctx: SagaContext) {
try {
while (true) {
if (ctx.socketChannel === null) {
break;
}
const events = yield flush(ctx.socketChannel);
const startedExecutingAt = performance.now();
if (Array.isArray(events)) {
const deltas = events.reduce(
(patch, event) => {
if (event.type === 'message') {
patch.bids.push(...event.payload.data?.bids);
patch.asks.push(...event.payload.data?.asks);
//
}
//
return patch;
},
{ bids: [], asks: [] } as SocketMessage
);
if (deltas.bids.length || deltas.asks.length) {
yield putResolve(receivedDeltas(deltas));
}
}
yield call(delayNextDispatch, startedExecutingAt);
}
} catch (error: any) {
reportError(error);
yield put(
disconnectedSocket({
reason: SocketStateReasons.UNKNOWN,
})
);
}
}
After Debugging I got the following:
The Thing is that when I Provide one Reducer to my store the channel works well and data is fetched where as when providing combinedReducers I am getting
an established connection from my handleConnectingSocket generator function
and an empty event array [] from
const events = yield flush(ctx.socketChannel) written in handleConnectedSocket
Tried to clarify as much as possible
ok so I start refactoring my typescript by changing the types, then saw all the places that break, there was a problem in my sagas.tsx.
Ping me if someone faced such an issue in the future
I would like to add a progress indicator to Tesseract.js logging.
The example in docs works just fine, until setting a state hook into logger:
const worker = createWorker({
logger: (m) => {
setProgress(m) //new
}});
...
const [ocr, setOcr] = useState('Recognizing...');
const [progress, setProgress] = useState(null); //new
...
return (
<div className="App">
<p>
<LogComponent progress={progress}/> //new
</p>
</div>);
This causes the browser to slow down significantly (probably due React's way to re-render on each state update). Is there a way to get around this? Using React.memo perhaps?
You could design it into a hook so your entire component doesn't re-render. Here is a useTesseract hook you can use that I created: https://gist.github.com/KevinDanikowski/25cdcdda2ef4750bcf443f2027cc375a
Copy and Pasted:
import { useState, useEffect } from 'react'
import { createWorker } from 'tesseract.js'
export default function useTesseract({ tesseractLanguage = 'eng', log = false }) {
const [tesseractWorker, setTesseractWorker] = useState(null)
const [loadingModel, setLoadingModel] = useState(true)
const [modelError, setModelError] = useState(false)
const [imgResults, setImgResults] = useState({})
const [processing, setProcessing] = useState(false)
const [progress, setProgress] = useState(0)
const extractTextFromImage = (imageUrl) => {
const recognize = async () => {
const {
data: {
hocr: htmlOutput,
text,
// tsv, box, unlv
},
} = await tesseractWorker.recognize(imageUrl)
setProcessing(false)
setImgResults({ html: htmlOutput, text })
}
if (loadingModel) {
try {
setTimeout(recognize, 400)
} catch (e) {
console.error('Timeout Error:', e.message)
setImgResults({ error: true })
}
} else {
try {
setProcessing(true)
recognize()
} catch (e) {
console.error('Tesseract Error:', e.message)
setProcessing(false)
setImgResults({ error: true })
}
}
}
const logger = (m) => {
setProgress(m.progress)
if (log) {
console.info(m)
}
}
useEffect(() => {
const loadTesseract = async () => {
if (tesseractWorker) {
await tesseractWorker.loadLanguage(tesseractLanguage)
await tesseractWorker.initialize(tesseractLanguage)
console.info(`INFO: loaded ${tesseractLanguage} tesseract model`)
} else {
const tesseractWorker = createWorker({
logger,
// specify paths because sometimes the free CDN goes down
// corePath: '/static/tesseract-core.wasm.2.2.0.js',
// workerPath: '/static/tesseract-worker.v2.1.4.min.js',
})
setTesseractWorker(tesseractWorker)
await tesseractWorker.load()
await tesseractWorker.loadLanguage(tesseractLanguage)
await tesseractWorker.initialize(tesseractLanguage)
console.info(`INFO: loaded ${tesseractLanguage} tesseract model`)
setLoadingModel(false)
setModelError(true)
setLoadingModel(false)
}
}
loadTesseract().catch((e) => {
console.error(`ERROR: Failed to load tesseract model`, e.message)
setModelError(true)
setLoadingModel(false)
})
// TODO: Have to add a ref to reference the latest tesseractWorker in order to terminate
// return () => tesseractWorker.terminate()
}, [tesseractLanguage])
return {
imgResults,
loadingModel,
processing,
modelError,
progress,
extractTextFromImage,
}
}
I managed to implement and render the progress of the tesseract worker by putting my application render inside a class and using the setState method:
class App extends React.Component {
constructor(props){
super(props)
this.state = {
file: null
}
this.handleChange = this.handleChange.bind(this)
}
setProgress(m) {
if (m.progress !== 0 && m.progress !== 0.5 && m.progress !== 1){
var prog = "Progress: " + Math.round(m.progress*100) + "%"
this.setState({progress: prog})
}
}
worker = createWorker({
logger: m => this.setProgress(m),
});
doOCR = async () => {
await this.worker.load();
await this.worker.loadLanguage('eng');
await this.worker.initialize('eng');
const { data: { text } } = await this.worker.recognize(this.state.file);
this.setState({text: extractTotal(text),
progress: ""});
};
handleChange(event) {
this.setState({text: placeholder});
this.setState({
file: URL.createObjectURL(event.target.files[0]),
})
this.doOCR()
}
setText(input){
if (!input) {
return "Please select a receipt"
}
else {
return input
}
}
render() {
console.log("Text: " + this.state.text)
return (
<div className="container">
<p>{this.setText(this.state.text)}</p>
<p>{this.state.progress}</p>
<input type="file" onChange={this.handleChange}/>
<img src={this.state.file} className='logo' alt=""/>
</div>
);
}
}
Could anyone can help me, because i can't understand why HOC is causing ifnite loop while I am using it on container. That's my container:
class UserContainer extends Component {
componentDidMount() {
const { onValuePassedThroughParams, match } = this.props;
const { user } = match.params;
if (user !== '') {
onValuePassedThroughParams(user);
}
}
render() {
const { user } = this.props;
return (
<UserView user={user} />
);
}
}
const UserContainerWithLoading = LoaderHOC(UserContainer);
const mapStateToProps = state => ({
user: state.user,
});
const mapDispatchToProps = dispatch => ({
onValuePassedThroughParams: val => dispatch(takeUserNameAndFetchData(val)),
});
export default
withRouter(
connect(mapStateToProps, mapDispatchToProps)(UserContainerWithLoading),
);
my HOC:
const LoaderHOC = WrappedComponent => props =>{
return(
props.user.isLoading
? <div className={styles.ldsHourglass} />
: <WrappedComponent {...props} />
)};
and also a thunk:
function fetchData(url) {
return (
fetch(url)
.then(result => result.json())
);
}
export default function takeUserNameAndFetchData(name) {
const userInfoUrl = `https://api.github.com/users/${name}`;
const userRepoUrl = `https://api.github.com/users/${name}/repos`;
return (dispatch) => {
dispatch(fetchUserBegin());
Promise.all([
fetchData(userInfoUrl),
fetchData(userRepoUrl),
])
.then(([info, repos]) => {
dispatch(fetchUserInfoSucces(info));
dispatch(fetchUserReposSuccess(repos));
dispatch(fetchUserLoadingEnd());
})
.catch((err) => {
console.log(`ERROR!${err}`);
dispatch(fetchUserError());
});
};
}
When I use an HOC on my View component everything is fine and it's stop geting data from the server, but when I am using it on container there is always an infinite loop. Do you have any advice for me?