Problem:
It doesn't work to use " const [data, setData] = useState<IHit[]>([]);" because I get the error "data.map is not a function"
What part am I missing?
Thank you!
import React, { useState, useEffect } from 'react';
import { render } from 'react-dom';
import axios from 'axios';
import './style.css';
import { IHit } from './responseTypes';
function App() {
const [data, setData] = useState<IHit[]>([]);
const [query, setQuery] = useState('react');
useEffect(() => {
let ignore = false;
console.log('useeffect: ', ignore);
async function fetchData() {
const result = await axios(
'https://hn.algolia.com/api/v1/search?query=' + query
);
if (!ignore) setData(result.data);
}
fetchData();
return () => {
ignore = true;
console.log('return: ', ignore);
};
}, [query]);
if (data.length === 0) return <div>Testtest</div>;
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>
{data.map(({ url, objectID, title }: IHit) => (
<a href={url}>
<li key={objectID}>{title}</li>
</a>
))}
</ul>
</div>
);
}
render(<App />, document.getElementById('root'));
https://stackblitz.com/edit/usereducer-typescript-state-uaft7n?file=index.tsx
Related
i have a minor problems, whenever user click on the shorturl the total number of clicks is increased by 2 instead of 1 and the user can see the homepage for a while before being completely redirected.
here is for index.js on account components
import { useState, Fragment, useEffect, useCallback, useMemo } from 'react';
import React from 'react';
import { Box, Grid, Typography, Button, Divider, Snackbar } from '#mui/material';
import Navbar from './Navbar';
import LinkCard from './LinkCard';
import ShortenUrlModal from './ShortenUrlModal';
import { app, firestore, auth } from '../../firebase';
import { nanoid } from 'nanoid';
import copy from 'copy-to-clipboard';
function Account() {
const [newLinkToastr, setNewLinkToastr] = useState(false);
const [openModal, setOpenModal] = useState(false);
const [links, setLinks] = useState([]);
const userUid = auth.currentUser.uid
const linksPathRef = useMemo(() => firestore.collection('users').doc(userUid).collection('links'), [userUid]);
const handleCreateShortenLink = async (name, longURL) => {
const link = {
name,
longURL,
createdAt: app.firestore.FieldValue.serverTimestamp(),
shortCode: nanoid(6),
totalClicks: 0
};
const resp = await linksPathRef.add(link);
setLinks((links) => [...links, { ...link, createdAt: new Date(), id: resp.id }])
setOpenModal(false)
};
useEffect(() => {
const fetchLinks = async () => {
const snapshot = await linksPathRef.get()
const tempLinks = [];
snapshot.forEach((doc) => tempLinks.push({ ...doc.data(), id: doc.id, createdAt: doc.data().createdAt.toDate() }));
setLinks(tempLinks);
}
fetchLinks()
},[linksPathRef]);
const handleDeleteLink = useCallback(async (linkDocID) => {
await linksPathRef.doc(linkDocID).delete()
setLinks(oldLinks => oldLinks.filter(link => link.id !== linkDocID))
}, [linksPathRef]);
const handleCopyLink = useCallback(shortUrl => {
copy(shortUrl);
setNewLinkToastr(true)
}, [])
return (
<>
<Snackbar open={newLinkToastr} onClose={() => setNewLinkToastr(false)} autoHideDuration={1000} message="Link Copied"/>
{openModal && <ShortenUrlModal createShortenLink={handleCreateShortenLink} handleClose={() => setOpenModal(false)} />}
<Navbar />
<Box mt={5}>
<Grid container justifyContent="center">
<Grid item xs={8}>
<Box mb={5} display="flex">
<Box mr={3}>
<Typography variant="h4">Links</Typography>
</Box>
<Button onClick={() => setOpenModal(true)} disableElevation variant="contained" color="primary">Create New</Button>
</Box>
{links.sort((prevLink, nextLink) => nextLink.createdAt - prevLink.createdAt).map((link, idx) => (
<Fragment key={link.id}>
<LinkCard {...link} deleteLink={handleDeleteLink} copyLink={handleCopyLink} />
{ idx !== links.length -1 && ( <Box my={4}>
<Divider />
</Box>
)}
</Fragment>
))}
</Grid>
</Grid>
</Box>
</>
)
}
export default Account
and this is for redirect components
import { useParams } from 'react-router-dom';
import { useEffect, useState, useCallback } from 'react';
import React from 'react';
import { firestore, app } from '../../firebase';
import { CircularProgress, Box, Typography } from '#mui/material';
function LinkRedirect() {
const { shortCode } = useParams();
const [loading, setLoading] = useState(true);
const fetchLinkDoc = useCallback(async () => {
try {
const linkDoc = await firestore.collection('links').doc(shortCode).get();
if (linkDoc.exists) {
const { longURL, linkID, userUid } = linkDoc.data();
await firestore
.collection('users')
.doc(userUid)
.collection('links')
.doc(linkID)
.update({
totalClicks: app.firestore.FieldValue.increment(1)
});
window.location.href = longURL;
} else {
setLoading(false);
}
} catch (error) {
console.error(error);
setLoading(false);
}
}, [shortCode]);
useEffect(() => {
fetchLinkDoc();
}, [fetchLinkDoc]);
if (loading)
return (
<Box mt={10} textAlign="center">
<CircularProgress />
<Typography>Redirecting to the link</Typography>
</Box>
);
else
return (
<Box mt={10} textAlign="center">
<Typography>Link is not valid</Typography>
</Box>
);
}
export default LinkRedirect;
i use react-router-dom v6 and firebase/firestore v9
I want the total clicks to increase by only 1 and I want the user to not see the homepage before being redirected.
Hello i am new to programming, and i have learn to use MERN to make a sign up,i have no issue with backend but when i tried to use redux ,i have this problem in the frontend
enter image description here
This is the code for the SignInScreen.js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
I dont know what wrong with the code,but i do know that it connected with the redux store which have reducer,action and constant..this is for the redux store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import {
userLoginReducer,
} from './reducers/userReducer'
const userInfo = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
const initialState = {
userLogin: { userInfo },
}
const reducer = combineReducers({
userLogin: userLoginReducer,
})
const middleware = [thunk]
const store = createStore(
reducer,
initialState,
compose(applyMiddleware(...middleware))
)
export default store
This is for constant
export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL'
export const USER_LOGOUT = 'USER_LOGOUT'
this is userReducer.js
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
USER_LOGOUT,
} from '../constants/userConstant'
function userLoginReducer(state = {}, action) {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true }
case USER_LOGIN_SUCCESS:
return { loading: false, userInfo: action.payload }
case USER_LOGIN_FAIL:
return { loading: false, error: action.payload }
case USER_LOGOUT:
return {}
default:
return state
}
}
export userLoginReducer
and lastly for user.js
import Axios from 'axios'
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
} from '../constants/userConstant'
const login = (email, password) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST })
const config = { headers: { 'Content-Type': 'application/json' } }
const { data } = await Axios.post(
'/api/users/login',
{ email, password },
config
)
dispatch({ type: USER_LOGIN_SUCCESS, payload: data })
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
export login
I just want to get the data into the userInfo but it dont recognized them and said it is a TypeError..I hope u can help me with this..im using the redux latest version
The problem is in userLoginReducer. Each reducer should return a complete new copy of the store.
If you return just the changes, the object you return replaces the entire state.
For example in this code:
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true };
}
The state { userLogin: { userInfo } } will be replaced with { loading: true }. Then you will not have userLogin anymore in the state. That's why you get the error.
To overcome this problem, spread the previous state in returned object (for all actions):
switch (action.type) {
case USER_LOGIN_REQUEST:
return { ....state, loading: true }; // ...state copies exist state to the new copy of state
}
Note: To easily solve similar bugs in the future, I recommend to use redux devtools extension. It is a great extension for debugging and look at changes in redux store.
I have tried that but still cannot fix my problem,this is the rest of my signinScreen,js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}
it still show the same error like before,i have no idea to solve this
I tried to implement a simple UI for smart contracts using polkadot.js in the next.js framework.
The content of the WEB UI is a simple one that calls the Flipper contract, which is famous for the sample contract of the substrate.
When compiling, the following error is output. Can you tell me how to solve it?
Souce Code:
import { useEffect, useState } from "react";
import {
web3Accounts,
web3Enable,
web3FromSource,
} from "#polkadot/extension-dapp";
import { InjectedAccountWithMeta } from "#polkadot/extension-inject/types";
const Home = () => {
const [allAccount, setAllAccount] = useState<InjectedAccountWithMeta[]>([]);
const getAccounts = async () => {
const extensions = await web3Enable("my cool dapp");
if (extensions.length === 0) {
return;
}
const allAccounts = await web3Accounts();
setAllAccount(allAccounts);
};
useEffect(() => {
getAccounts();
}, []);
return (
<>
<div>
{typeof allAccount !== "undefined"
? allAccount.map((account) => {
return (
<div key={account.address}>
<div className="font-bold mb-2 text-white">
{account.address}
</div>
</div>
);
})
: ""}{" "}
</div>
</>
);
};
export default Home;
Error Information:
> Build error occurred
ReferenceError: window is not defined
at file:///Users/shin.takahashi/develop/substrate/flipper_frontend/fillper_frontend/node_modules/#polkadot/extension-dapp/bundle.js:10:13
at ModuleJob.run (node:internal/modules/esm/module_job:175:25)
at async Loader.import (node:internal/modules/esm/loader:178:24)
at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15) {
type: 'ReferenceError'
}
This issue occurs because next.js is a framework for server-side rendering.
In order to avoid this problem, it is necessary to control not to execute
server-side rendering for the relevant part.
Componentize the part that gets account information from the relevant Extension.
Adopt dynamic import when using this component and set server-side rendering to off.
component sample code:
import { useEffect, useState } from "react";
import { web3Accounts, web3Enable } from "#polkadot/extension-dapp";
import { InjectedAccountWithMeta } from "#polkadot/extension-inject/types";
const Extention = () => {
const [allAccount, setAllAccount] = useState<InjectedAccountWithMeta[]>([]);
const getAccounts = async () => {
const extensions = await web3Enable("my cool dapp");
if (extensions.length === 0) {
return;
}
const allAccounts = await web3Accounts();
setAllAccount(allAccounts);
};
useEffect(() => {
getAccounts();
}, []);
return (
<>
<div>
{typeof allAccount !== "undefined"
? allAccount.map((account) => {
return (
<div key={account.address}>
<div className="font-bold mb-2 text-white">
{account.address}
</div>
</div>
);
})
: ""}{" "}
</div>
</>
);
};
export default Extention;
Calling component sample code:
import dynamic from "next/dynamic";
import { useState } from "react";
const Extention = dynamic(() => import("../component/extention"), {
ssr: false,
});
const Home = () => {
const [showExtention, setShowExtention] = useState(false);
return (
<>
<button onClick={() => setShowExtention(true)}>show extention</button>
{showExtention == true && <Extention></Extention>}
</>
);
};
export default Home;
Hello I have basic code with useSelector, where data from the useEffect is stored into currentItems.
How to iterate through the state array and render it?
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getToDoItems, setItems } from "../redux/globalSlice";
const ToDoList = () => {
const dispatch = useDispatch();
const { currentItems } = useSelector((state) => state.global);
useEffect(() => {
async function getToDoItems(id) {
const response = await fetch(
`https://xxx.mockapi.io/api/list/${id}/todos`
);
const data = await response.json();
dispatch(setItems(data));
}
getToDoItems(2);
}, []);
return (
<div>
<h1>ahoj</h1>
{currentItems.map((todo) => {
<div>{todo.title}</div>;
})}
</div>
);
};
export default ToDoList;
I understand that this is an asynchronous operation, but I don't know how to render it until after loading and storing data to store from the api.
I'm new to Redux. It's really confusing to understand basic syntax. None of the bugs are found so It's hard to figure out what's wrong with my code.
It worked well last week, I don't remember what I have changed.
//child component
import React, { Component } from 'react';
import { SingleDatePicker } from 'react-dates';
import moment from 'moment';
class InputForms extends Component {
state = {
inputs: ['input-0'],
title: '',
tag: '',
createdAt: moment(),
imageLinks: [''],
calendarFocused: false,
error: '',
}
appendInput(e) {
const newInput = `input-${this.state.inputs.length}`;
this.setState({ inputs: this.state.inputs.concat([newInput]) });
}
onTitleChange = (e) => {
const title = e.target.value;
this.setState(() => ({ title }));
};
onTagChange = (e) => {
const tag = e.target.value;
this.setState(() => ({ tag }));
};
onImageLinkChange = (e) => {
const imageLinks = e.target.value;
this.setState(() => ({ imageLinks: this.state.imageLinks.concat([imageLinks]) }));
};
onDateChange = (createdAt) => {
if (createdAt) {
this.setState(() => ({ createdAt }));
}
};
onFocusChange = ({ focused }) => {
this.setState(() => ({ calendarFocused: focused }));
};
onSubmit = (e) => {
e.preventDefault();
if (!this.state.title || !this.state.imageLinks) {
this.setState(() => ({ error: '제목과 이미지링크를 입력해주세요' }));
} else {
this.setState(() => ({ error: '' }));
this.props.onSubmit({
title: this.state.title,
tag: this.state.tag,
createdAt: this.state.createdAt.valueOf(),
imageLinks: this.state.imageLinks,
});
}
}
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
placeholder="제목을 입력하세요"
required
value={this.state.title}
onChange={this.onTitleChange}
/>
<input
type="text"
placeholder="태그를 입력하세요"
value={this.state.tag}
onChange={this.onTagChange}
/>
<SingleDatePicker
date={this.state.createdAt}
onDateChange={this.onDateChange}
focused={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
numberOfMonths={1}
isOutsideRange={() => false}
/>
{this.state.inputs.map((input, key) => {
return <input
key={input}
type="text"
required
value={this.state.imageLinks}
onChange={this.onImageLinkChange}
placeholder={`이미지링크 ${key + 1}`}
/>
})}
<button>저장</button>
</form>
</div>
)
}
}
export default InputForms;
//parent component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import configureStore from '../store/configureStore';
import InputForms from './InputForms';
import { addPost } from '../actions/posts';
const store = configureStore();
class CreatePost extends Component {
onSubmit = (post) => {
this.props.addPost(post);
this.props.history.push('/');
};
render(){
return(
<div>
<InputForms onSubmit={this.onSubmit}/>
</div>
)
}
}
const mapDispatchToProps = (dispatch, props) => ({
addPost: (post) => dispatch(addPost(post))
});
export default connect(undefined, mapDispatchToProps)(CreatePost);
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
import registerServiceWorker from './registerServiceWorker';
import AppRouter from './routers/AppRouter';
import 'normalize.css/normalize.css';
import './style/style.css';
import 'react-dates/lib/css/_datepicker.css';
import 'react-dates/initialize';
const store = configureStore();
const jsx = (
<Provider store={store}>
<AppRouter />
</Provider>
);
ReactDOM.render(jsx, document.getElementById('root'));
registerServiceWorker();
//action
import database from '../firebase/firebase';
//Add Posts
export const addPost = (post) => ({
type: 'ADD_POST',
post
});
//reducer
const postReducerDefaultState = [];
export default (state = postReducerDefaultState, action) => {
switch (action.type) {
case 'ADD_POST':
return [
...state,
action.post
];
default:
return state;
}
};
In your reducer, you return as below
return [ ...state, action.post];
Reducer doesnt return array, but instead returning objects. Secondly, action.post is a value, you need to assign this to key, something like below:
return { ...state, post: action.post };