ReactJS force useState to set immediately appropriate value - reactjs

I have a React app with selecting logic but everything in this logic breaks because of "useState()". I know that "useState()" is asynchronous and value is not assigned immediately but to work my logic value is needed immediately when I click on the row. And the question is how to set value immediately in the "setEntry()" ?
This is my code:
const TableRow = (props) => {
const [entry, setEntry] = useState('');
const array = Object.entries(props);
const navigate = useNavigate();
let previousRow;
const deleteContact = async (e) => {
e.preventDefault();
const id = e.target.getAttribute('id');
await requester(urls.accountWithId + '/' + id, methods.delete);
await requester(urls.contacts + '/' + id, methods.delete)
.then(() => {
navigate(urls.mainPage);
notificationsReceiver('Contact is deleted successfully!');
})
.catch((e) => {
alert(e.message);
});
};
const checkIsSelected = (id) => {
if (entry === id) {
setEntry('');
return;
}
setEntry(id);
};
const changeStyle = (e) => {
const currentRowId = e.currentTarget.getAttribute('id');
if (entry === '') {
e.currentTarget.style.backgroundColor = '#E3E5E7';
setEntry(currentRowId);
previousRow = e.currentTarget;
}
else if (entry === currentRowId) {
e.currentTarget.style.backgroundColor = '#F5F7FA';
setEntry('');
}
else if (entry !== currentRowId) {
setEntry(currentRowId);
e.currentTarget.style.backgroundColor = '#E3E5E7';
previousRow.style.display = '#F5F7FA';
previousRow = e.currentTarget;
}
};
const navigateToDetails = (e) => {
e.preventDefault();
const id = e.target.getAttribute('id');
navigate(urls.details + '/:' + id);
};
const editContact = async (e) => {
e.preventDefault();
const id = e.target.getAttribute('id');
navigate(urls.editContact + '/:' + id);
}
return (
<tr className={styles['contact-row']} id={array[1][1].id} onDoubleClick={navigateToDetails} key={array[1][1].id} onClick={(e) => { checkIsSelected(array[1][1].id); changeStyle(e); }}>
<td id={array[1][1].id}>{array[1][1].name}</td>
<td id={array[1][1].id}>{array[1][1].continentAndCountry}</td>
<td id={array[1][1].id}>{array[1][1].email}</td>
<td id={array[1][1].id}><a href={array[1][1].baseUrlForFreeGuyz + '/' + array[1][1].accountNameForFreeGuyz}>{array[1][1].accountNameForFreeGuyz}</a></td>
<td id={array[1][1].id}><a href={array[1][1].baseUrlForInstagram + '/' + array[1][1].accountNameForInstagram}>{array[1][1].accountNameForInstagram}</a></td>
<td id={array[1][1].id}><a href={array[1][1].baseUrlForTwitter + '/' + array[1][1].accountNameForTwitter}>{array[1][1].accountNameForTwitter}</a></td>
<td id={array[1][1].id}>{array[1][1].accountType}</td>
<td>
{entry && <>
<button className={`btn btn-warning ${styles['edit-button']}`} id={array[1][1].id} onClick={editContact}>Edit</button>
<button className={`btn btn-danger ${styles['delete-button']}`} id={array[1][1].id} type="submit" onClick={deleteContact}>Delete</button></>}
</td>
</tr>
);
}
export default TableRow;
I'll be grateful if anyone can help me.

Instead of continuously calling setSentry for state updates, you can use a local variable to check value changes and after all, you can update entry at once.
const changeStyle = (e) => {
//local variable to proceed the internal logic
let updatedEntry = e.currentTarget.getAttribute('id')
if (updatedEntry === currentRowId) {
e.currentTarget.style.backgroundColor = '#F5F7FA';
updatedEntry = ""
} else {
updatedEntry = currentRowId
e.currentTarget.style.backgroundColor = '#E3E5E7';
previousRow.style.display = '#F5F7FA';
previousRow = e.currentTarget;
}
currentRowId = e.currentTarget.getAttribute('id');
//only update `entry` state once after all
setEntry(updatedEntry)
};

Related

Not working pagination after search -React

I use to react-paginate package for paginate. After searching, the pagination pattern is broken. While there should be 5 items per page, this situation breaks down after the search. I share my ss and codes. thanks for your time.
here is default paginate:
and after search paginate:
and here is my code:
const displayUsers = (users, setUsers, userCurrentPage) => { // pagination configs
const startIndex = (userCurrentPage - 1) * 5
const endIndex = userCurrentPage * 5
const productsToDisplay = users.slice(startIndex, endIndex)
setUsers(productsToDisplay)
}
const handleSearch = (e) => { // filter script
let searchValue = e.target.value
let filteredTasks = users.filter((task) => {
return task.UserID.toLowerCase().includes(searchValue.toLowerCase())
})
setPaginationUsers(filteredTasks)
}
useEffect(() => {
if (searchfield === '') {
setPaginationUsers(users)
} else {
const dynamicFilter = users.filter((user) => {
return user.UserID.toLowerCase().includes(searchfield.toLowerCase())
})
setPaginationUsers(dynamicFilter)
}
}, [searchfield])
// And here is mapping area
{paginationUsers.map((userDetail, index) => {
const {
UserID,
Country,
City,
MMA,
Time,
Game,
Revenue,
Timezone,
Device_Model,
Ad_Type,
SubNetwork,
} = userDetail
if (typeof userDetail.Revenue === 'string') {
userDetail.Revenue = parseFloat(userDetail.Revenue).toFixed(6)
}
return (
<tr key={index}>
<td>{UserID}</td>
<td>{Country}</td>
<td>{City}</td>
<td>{MMA}</td>
<td>{SubNetwork}</td>
<td>{Time}</td>
<td>{Revenue}</td>
<td>{Game}</td>
<td>{Timezone}</td>
<td>{Device_Model}</td>
<td>{Ad_Type}</td>
</tr>
)
})}

React - autocomplete search for API - array always returns empty

I'm trying to make an autocomplete search for Alpha Vantage API, but the array which should contain the matches (suggestion) always returns empty when I type in the input field and I don't know what could be the reason, I'm stuck for a while on this and would appreciate if someone could help me with this.
The related code here is mostly in useEffect and the inputHandler:
const Search = () => {
useEffect(() => {
const getSymbols = async () => {
const searchURL = `https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
const res = await axios.get(searchURL);
if(res) {
setSecurity(res.data.bestMatches);
if(security !== undefined && security.length > 0) {
let symbols = security.map(sec => sec['1. symbol'])
setAllSymbol(symbols);
}
}
}
getSymbols();
}, [])
console.log(allSymbol);
const inputHandler = (e) => {
setTextInput(e.target.value);
let matches = [];
if(textInput.length > 0) {
matches = allSymbol.filter(sym => {
const regex = new RegExp(`${textInput}`, "gi");
return sym.match(regex);
})
setSuggestion(matches);
}
console.log(suggestion);
setTextInput(e.target.value);
}
const showData = async (e) => {
e.preventDefault();
const searchURL = `https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
const monthlyURL = `https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=${textInput}&apikey=${process.env.REACT_APP_ALPHA_VANTAGE_API_KEY}`
try {
const res = await axios.get(searchURL);
const data = await axios.get(monthlyURL);
if(res) {
setTickers(res.data.bestMatches[0]);
setSymbol(res.data.bestMatches[0]['1. symbol']);
setSecurity(res.data.bestMatches);
if(data) {
const monthlyTimeSeries = Object.values(data.data['Monthly Time Series']);
const result = [monthlyTimeSeries[1]];
const resultValues = Object.keys(result[0]).map(key => {
return Math.floor(result[0][key]);
})
setPrices(resultValues);
}
}
} catch(err) {
console.log(err)
}
setDailyPrices([]);
setWeeklyPrices([]);
setIntraPrices([]);
}
return (
<StyledSearch>
<div className="wrapper">
<h1>Security Price Monitor App </h1>
<form onSubmit={showData} className="search-form">
<input type="text" value={textInput} onChange={inputHandler} placeholder='Enter Stock Symbol (GOOG, MSFT)'/>
<button type="submit">Search</button>
</form>
</div>
{prices.length > 0 && (
<>
<Table prices={prices} symbol={symbol}/>
<TimeFrames symbol={symbol} textInput={textInput} weeklyPrices={weeklyPrices} setWeeklyPrices={setWeeklyPrices} dailyPrices={dailyPrices} setDailyPrices={setDailyPrices} intraPrices={intraPrices} setIntraPrices={setIntraPrices} />
</>
)}
</StyledSearch>
)
}

I have a question about react array clearing

There is an array in the parent class(TodolistActivity), and the child class(TaskCallFunc) displays the elements in the array. When I use a.list= []; to clear the array, there is no clearing on the page
but a.list.length = 0 is ok. why?
Here is my code:
interface IListData {
list: IActivityData[]
}
interface IActivityData {
id: number,
content: string,
finish: boolean
}
export function TodolistActivity(activty: IListData) {
const [acty, setActivity] = useState(activty);
const [content, setContent] = useState('');
const input_ref = React.createRef<HTMLInputElement>();
const [selectCount, setSelect] = useState(0);
const handleAdd = () => {
if (input_ref.current) {
if (input_ref.current.value === '') {
alert("输入内容 不能为空!");
return;
}
let id = acty.list.length;
acty.list.unshift({ id: id, content: content, finish: false })
let a = { ...acty }
setActivity(a);
input_ref.current.value = "";
}
}
const calcuateSelect = () => {
let a = acty.list.filter((v, i) => { return v.finish === true })
setSelect(a.length);
}
const handleChange = (input: React.ChangeEvent<HTMLInputElement>) => {
setContent(input.target.value);
}
const clearTask = () => {
let a = { ...acty};
a.list= [];
//a.list.length = 0;
setActivity(a);
}
return (
<div>
<input type='text' onChange={handleChange} ref={input_ref} />
<button className="add task" onClick={handleAdd}>add task</button>
<button className="clear task" onClick={clearTask}>clear task</button>
{console.log(acty)}
<TaskCallFunc data={acty} action={() => { calcuateSelect() }} />
<br />
<label htmlFor="">select{selectCount}/{acty.list.length}</label>
</div>
);
}
interface ItaskCell {
data: IListData,
action: () => void
}
function TaskCallFunc(taskData: ItaskCell) {
const [data, setData] = useState(taskData);
const HandleSlecet = (x: number) => {
for (let index = 0; index < data.data.list.length; index++) {
if (data.data.list[index].id === x) {
let newState = { ...data };
newState.data.list[index].finish = !data.data.list[index].finish;
setData(newState);
data.action();
}
}
}
const handleMap = () => {
return data.data.list.map((v, i) => { return <li key={v.id}>{v.id}: {v.content} <input type="checkbox" checked={v.finish} onChange={() => { HandleSlecet(v.id) }} /> </li> });
}
return (
<ul>{handleMap()}</ul>
);
}
If you know the answer, please let me know thank you
TaskCallFunc component doesn't "listen" for changes on the taskData prop to update the local copy stored in state. Use an useEffect hook with a dependency on taskData prop to update the state when it changes.
function TaskCallFunc(taskData: ItaskCell) {
const [data, setData] = useState(taskData);
useEffect(() => {
setData(taskData);
}, [taskData]);
...
You can clear the array easely by doing setActivity({ list: [] }) consider also to add useEffect as Drew says to listen for changes

Table empties when state updated, need to refresh it

When I change a value in the table, it disappears when I update the state with the new value. I have checked state and it is updated correctly but the table is now blank. I am using a bootstrap table.
Everything in state is now updated but the grid is empty, can I use useEffect to do rebind the table?
const GridEdit = () => {
const store = useContext(StoreContext);
const handleBlur = (e, arrayRow, editableFields) => {
const newVals = store.gridItems[0][0];
newVals["Status"] = e.target.innerHTML;
store.gridItems[1](newVals);
console.log("hello ", store.gridItems[0]);
};
const dataTable = store.gridItems[0];
function tableHeaders(data) {
let tableHeaders = [];
if (data.length > 0) {
let headers = Object.keys(data[0]);
headers.map((header) =>
tableHeaders.push(<th key={header}>{header}</th>)
);
}
if (tableHeaders.length === 0) {
return null;
} else return tableHeaders;
}
function tableRows(dataTable) {
let tableLength = dataTable.length;
let table = [];
for (let i = 0; i < tableLength; i++) {
let children = [];
let row = Object.values(dataTable[i]);
const readOnlyFields = row.slice(0, 4);
const editableFields = row.slice(4, 7);
readOnlyFields.map((data) => children.push(<td id={row[0]}>{data}</td>));
editableFields.map((data) =>
children.push(
<td ContentEditable="true" id={row[0]}>
{data}
</td>
)
);
table.push(
<tr key={row} onBlur={(e) => handleBlur(e, i, editableFields)}>
{children}
</tr>
);
}
if (table.length === 0) {
return null;
} else {
return table;
}
}
return (
<tbody className="tableHeaders">
<tr>{tableHeaders(dataTable)}</tr>
{tableRows(dataTable)}
</tbody>
);
};
export default GridEdit;

When I use async in componentDidMount, Component will Mount and Unmount again and again. Why?

When I call getCityName component will unmount and DidMount again and again, unless I remove async .All the code is running in nextjs.
this.state = {
bank_account: {
// bank_name: '',
// number: '',
// city: '',
// branch_name: ''
},
allCity: []
};
componentDidMount() {
const { owner_cellphone } = this.props;
this.getDraft(owner_cellphone);
this.fetchCity();
}
fetchCity = async () => {
const { data, error } = await getCity();
if (error) {
return;
}
console.log(data);
this.setState({ allCity: data });
};
getCityName = cityString => {
const { allCity } = this.state;
console.log(allCity);
if (!allCity || !cityString) {
return;
}
const cityArray = cityString.split(' ');
console.log(cityArray);
const targetProvince = allCity.find(item => item.code === cityArray[0]);
const targetCity = targetProvince.children.find(item => item.code === cityArray[0]);
return targetProvince.name + targetCity.name;
};
render() {
const { bank_account } = this.state;
const cityValue = this.getCityName(bank_account.city);
return (
<Item label="开户城市" icon={<Icon type="arrow-right" />} onClick={this.showCitySelect}>
<input
className="item-picker-input"
value={cityValue}
/>
</Item>
);
}
The reason it's not working because you are calling a async function from a sync function.
I am not sure it would work, but you can try..
getCityName = async (cityString) => {
const { allCity } = this.state;
console.log(allCity);
if (!allCity || !cityString) {
return;
}
const cityArray = cityString.split(' ');
console.log(cityArray);
const targetProvince = allCity.find(item => item.code === cityArray[0]);
const targetCity = targetProvince.children.find(item => item.code === cityArray[0]);
return targetProvince.name + targetCity.name;
};
render = async () => {
const { bank_account } = this.state;
const cityValue = await this.getCityName(bank_account.city);
return (
<Item label="开户城市" icon={<Icon type="arrow-right" />} onClick={this.showCitySelect}>
<input
className="item-picker-input"
value={cityValue}
/>
</Item>
);
}
getCityName = cityString => {
const { allCity } = this.state;
if (allCity === [] || !cityString) {
return;
}
const cityArray = cityString.split(' ');
let targetProvince = allCity.find(item => item.code === cityArray[0]);
if (targetProvince) {
let newProvince = JSON.parse(JSON.stringify(targetProvince));
const targetCity = newProvince.children.find(item => item.code === cityArray[1]);
return `${targetProvince.name} ${targetCity.name}`;
}
return '';
};
I think it might be a problem of deep copy.

Resources