So, I have a functional component with a couple of text boxes (Name and Timer i/p box display) and Start/Stop buttons.
I want that;
When Start is clicked, timer box should count and show seconds passed.
When Stop is clicked, name and time should be saved to redux store and timer i/p box should go back to zero. (Saved values for name/timer are displayed below as part of another component)
When focus is on timer box, timer should stop/pause
When we focus out/blur from timer box, timer should resume from that point.
I have added my code to sandbox in the below link.
https://codesandbox.io/s/crisil-tp7pv?file=/src/TestComponent.js
Some part of relevant code highlighted below as well. Essentially, I wanted to understand, if I am going right by using the useEffect to update the timer box?
Any suggestions to improve the approach?
export const TestComponent = (props) => {
const [task, setTask] = useState({ id: 0, taskName: "", timeField: 0 });
const [stopTimerEvt, setStopTimerEvt] = useState(true);
//var timerId;
useEffect(() => {
var seconds = 0;
var interval;
if (stopTimerEvt === false) {
interval = setInterval(function () {
setTask((prevState) => {
return { ...prevState, timeField: seconds++ };
});
}, 1000);
}
return () => clearInterval(interval);
}, [stopTimerEvt]);
const startTimer = () => {
setStopTimerEvt(false);
/*
timerId = setInterval(function () {
setTask((prevState) => {
return { ...prevState, timeField: seconds++ };
});
}, 1000);
*/
};
const stopTimer = () => {
//clearInterval(timerId);
setStopTimerEvt(true);
props.saveTask(task);
setTask((prevState) => {
return { ...prevState, timeField: 0 };
});
};
const handleChange = (e) => {
setTask((prevState) => ({
...prevState,
[e.target.id]: e.target.value
}));
};
return (
<main>
<input
type="text"
id="taskName"
value={task.taskName}
onChange={handleChange}
/>
<input
type="number"
id="timeField"
value={task.timeField}
onChange={handleChange}
/>
<button id="start" onClick={startTimer}>
Start
</button>
<button id="stop" onClick={stopTimer}>
Stop
</button>
</main>
);
};
export default connect(null, { saveTask })(TestComponent);
here is some of the changes you need to make in the TestComponent:
export const TestComponentNew = (props) => {
const [task, setTask] = useState({ taskName: "", timeField: 0 });
const timer = useRef();
useEffect(() => {
return () => stopTimer();
}, []);
const startTimer = () => {
timer.current = setInterval(function () {
setTask((v) => ({ ...v, timeField: +v.timeField + 1 }));
}, 1000);
};
const stopTimer = () => {
clearInterval(timer.current);
timer.current = null;
props.saveTask(task);
setTask({ taskName: "", timeField: 0 });
};
const handleChange = (e) => {
setTask((prevState) => ({
...prevState,
[e.target.id]: e.target.value
}));
};
return (
<main>
<input
type="text"
id="taskName"
value={task.taskName}
onChange={handleChange}
placeholder="task name"
/>
<input
type="number"
id="timeField"
value={task.timeField}
onChange={handleChange}
/>
<button id="start" onClick={startTimer}>
Start
</button>
<button id="stop" onClick={stopTimer}>
Stop
</button>
</main>
);
};
Here is the Demo: https://codesandbox.io/s/crisil-forked-pfrb3?file=/src/TestComponentNew.js
EDITED
export const TestComponentNew = (props) => {
const [task, setTask] = useState({ taskName: "", timeField: 0 });
const timer = useRef();
useEffect(() => {
return () => stopTimer();
}, []);
const startTimer = () => {
timer.current = setInterval(function () {
setTask((v) => ({ ...v, timeField: +v.timeField + 1 }));
}, 1000);
};
const stopTimer = () => {
clearInterval(timer.current);
timer.current = null;
props.saveTask(task);
setTask({ taskName: "", timeField: 0 });
};
const handleChange = (e) => {
setTask((prevState) => ({
...prevState,
[e.target.id]: e.target.value
}));
};
const handlePause = () => {
clearInterval(timer.current);
};
return (
<main>
<input
type="text"
id="taskName"
value={task.taskName}
onChange={handleChange}
placeholder="task name"
/>
<input
type="number"
id="timeField"
value={task.timeField}
onChange={handleChange}
onFocus={handlePause}
onBlur={startTimer}
/>
<button id="start" onClick={startTimer}>
Start
</button>
<button id="stop" onClick={stopTimer}>
Stop
</button>
</main>
);
};
Related
Currently I am trying to write TC for a function that is idle Time out Container
I am able to get 50 % coverage but nnote able to target the custom function can any one help me out
My Function
const IdleTimeoutContainer = props => {
const { logoutUser, usrProfId } = props;
const timeout = 1000 * 20;
const promptTimeout = 1000 * 60;
const userProfileID = usrProfId;
const [open, setOpen] = useState(false);
const [promptRemainingTimeout, setPromptRemainingTimeout] = useState(60);
const [remaining, setRemaining] = useState(0);
const onPrompt = () => {
setOpen(true);
setRemaining(promptTimeout);
setPromptRemainingTimeout(60);
};
const onIdle = () => {
setOpen(false);
setRemaining(0);
toastFunc({
content: 'Logged Off due to Idle Time',
config: { className: 'toast-container reject' },
toastType: 'error'
});
logoutUser();
};
const onActive = () => {
setOpen(false);
setRemaining(0);
};
const { getRemainingTime, isPrompted, activate } = useIdleTimer({
timeout,
promptTimeout,
onPrompt,
onIdle,
onActive
});
const handleStillHere = () => {
extendSessionApi()
.then(() => {
setOpen(false);
activate();
})
.catch(err => {
setOpen(false);
if (err.response.status) {
toastFunc({
content: err.response.data.errorMessage,
config: { className: 'toast-container reject' },
toastType: 'error'
});
logoutUser();
}
});
};
const handleLogOff = () => {
setOpen(false);
toastFunc({
content: 'Logged Off',
config: { className: 'toast-container reject' },
toastType: 'error'
});
logoutUser();
};
useEffect(() => {
const interval = setInterval(() => {
if (isPrompted()) {
setRemaining(Math.ceil(getRemainingTime() / 1000));
setPromptRemainingTimeout(Math.ceil(promptRemainingTimeout - 1));
}
}, 1000);
return () => {
clearInterval(interval);
};
}, [getRemainingTime, isPrompted, promptRemainingTimeout]);
return (
<div data-test='idle-timeout-container'>
{userProfileID && (
<Modal
data-test='idle-timeout-modal'
isOpen={open}
submitBtnLabel='Log-off'
onSubmit={handleLogOff}
cancelBtnLabel='Keep me Logged-in'
onCancel={handleStillHere}
>
<div className='circularProgressWithLabel'>
<span>
Are You Still There? Your session is about to expire due to <br />
inactivity in approximately {promptRemainingTimeout} seconds.
</span>
<Box position='relative' display='inline-flex'>
<CircularProgress
variant='static'
size={100}
thickness={5}
color='primary'
value={(promptRemainingTimeout / 60) * 100}
/>
<Box
top={0}
left={0}
bottom={0}
right={0}
position='absolute'
display='flex'
alignItems='center'
justifyContent='center'
>
<Typography variant='h2' component='div' color='primary'>
<b>{`${promptRemainingTimeout}`}</b>
</Typography>
</Box>
</Box>
</div>
</Modal>
)}
</div>
);
};
export default IdleTimeoutContainer;
test case file
const setup = (props = {}) => {
return shallow(<IdleTimeoutContainer {...props} />);
};
describe('Should render idle-timeout-container correctly ', () => {
let wrapper;
let onCancel;
let onConfirm;
let props;
beforeEach(() => {
const props = {
getUser: jest.fn,
logoutUser: jest.fn,
usrProfId: '1234',
submitBtnLabel:'Log-off',
cancelBtnLabel:'Keep me Logged-in',
onCancel,
onConfirm
};
wrapper = setup(props);
});
it('should render idle-timeout-container', () => {
const component = findComponentByTestAttr(
wrapper,
'idle-timeout-container'
);
expect(component).toHaveLength(1);
});
it('should render idle-timeout-modal', () => {
const component = findComponentByTestAttr(wrapper, 'idle-timeout-modal');
expect(component).toHaveLength(1);
});
it('should render idle-timeout-modalcontainer', () => {
const component = findComponentByTestAttr(wrapper, 'idle-timeout-modal');
component.simulate('submit');
expect(onConfirm);
});
it('should render idle-timeout-modalcontainer', () => {
const component = findComponentByTestAttr(wrapper, 'idle-timeout-modal');
component.simulate('cancel');
expect(onCancel);
});
});
i am not able to cover the TC before the Return, as it is custom code, can anyone help me please need to learn aleast one so that I can work it out
I have the following issue with website where the settings state resets after running more searches. The settings component is show below in the picture, it usually works but if you uncheck a box and then run a few more searches at some point the showAllDividends setting will be set to false, the All dividends component won't be on the screen, but for some reason the checkbox itself is checked (true). This is my first time really working with checkboxes in React, and I think I'm using the onChange feature wrong. Right now I get the event.target.checked boolean, but only onChange.
If that isn't the issue then the most likely cause is the default statements being run again on another render:
const [showMainInfo, setShowMainInfo] = useState(true);
const [showYieldChange, setShowYieldChange] = useState(true);
const [showAllDividends, setShowAllDividends] = useState(true);
the thing is I don't see why the default statements would run more than once, the component isn't being destroyed there's no react router usage. I expected it to keep its current state after the page is first loaded. I think the settings defaults are being rerun, but just don't understand why they would.
I unchecked, checked, unchecked the 'All dividends' checkbox, and it was unchecked when I ran 2 more searches. After the second search the checkbox was checked but the component was gone, because showAllDividends was false
main component SearchPage.js:
import React, {useState, useEffect} from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
import SearchBar from './SearchBar';
import AllDividendsDisplay from './dividend_results_display/AllDividendsDisplay';
import DividendResultsDisplay from './dividend_results_display/DividendResultsDisplay';
import SettingsView from './settings/SettingsView';
const HOST = process.env.REACT_APP_HOSTNAME
const PROTOCOL = process.env.REACT_APP_PROTOCOL
const PORT = process.env.REACT_APP_PORT
const BASE_URL = PROTOCOL + '://' + HOST + ':' + PORT
const SearchPage = ({userId}) => {
const DEFAULT_STOCK = 'ibm';
const [term, setTerm] = useState(DEFAULT_STOCK);
const [debouncedTerm, setDebouncedTerm] = useState(DEFAULT_STOCK);
const [loading, setLoading] = useState(false);
const [recentSearches, setRecentSearches] = useState([DEFAULT_STOCK]);
const [dividendsYearsBack, setDividendsYearsBack] = useState('3');
const [debouncedDividendYearsBack, setDebouncedDividendYearsBack] = useState('3');
const [errorMessage, setErrorMessage] = useState('');
const [dividendsData, setDividendsData] = useState(
{
current_price: '',
recent_dividend_rate: '',
current_yield: '',
dividend_change_1_year: '',
dividend_change_3_year: '',
dividend_change_5_year: '',
dividend_change_10_year: '',
all_dividends: [],
name: '',
description: '',
}
)
const [settingsViewVisible, setSettingsViewVisible] = useState(false);
const [showMainInfo, setShowMainInfo] = useState(true);
const [showYieldChange, setShowYieldChange] = useState(true);
const [showAllDividends, setShowAllDividends] = useState(true);
const onTermUpdate = (term) => {
setTerm(term)
}
// TODO: write a custom hook that debounces taking the term and the set debounced term callback
useEffect(() => {
const timerId = setTimeout(() => {
setDebouncedTerm(term);
}, 1500);
return () => {
clearTimeout(timerId);
};
}, [term]);
useEffect(() => {
const timerId = setTimeout(() => {
setDebouncedDividendYearsBack(dividendsYearsBack);
}, 1500);
return () => {
clearTimeout(timerId);
};
}, [dividendsYearsBack]);
useEffect(() => {runSearch()}, [debouncedTerm]);
useEffect(() => {
// alert(dividendsYearsBack)
if (dividendsYearsBack !== '') {
runSearch();
}
}, [debouncedDividendYearsBack])
useEffect(() => {
console.log("user id changed")
if (userId) {
const user_profile_api_url = BASE_URL + '/users/' + userId
axios.get(user_profile_api_url, {})
.then(response => {
const recent_searches_response = response.data.searches;
const new_recent_searches = [];
recent_searches_response.map(dict => {
new_recent_searches.push(dict.search_term)
})
setRecentSearches(new_recent_searches);
})
.catch((error) => {
console.log("error in getting user profile: ", error.message)
})
}
}, [userId])
useEffect(() => {
const user_profile_api_url = BASE_URL + '/users/' + userId
const request_data = {searches: recentSearches}
axios.post(user_profile_api_url, request_data)
// .then(response => {
// console.log(response)
// })
}, [recentSearches])
const makeSearchApiRequest = () => {
let dividends_api_url = BASE_URL + '/dividends/' + term + '/' + dividendsYearsBack
if (!recentSearches.includes(term)) {
setRecentSearches([...recentSearches, term])
}
axios.get(dividends_api_url, {})
.then(response => {
// console.log(response)
setLoading(false);
setDividendsData(response.data);
})
.catch((error) => {
console.log(error.message);
setLoading(false);
setErrorMessage(error.message);
})
}
const runSearch = () => {
console.log("running search: ", term);
setErrorMessage('');
if (term) {
setLoading(true);
if (!dividendsYearsBack) {
setDividendsYearsBack('3', () => {
makeSearchApiRequest()
});
} else {
makeSearchApiRequest()
}
}
}
const recentSearchOnClick = (term) => {
setTerm(term);
setDebouncedTerm(term);
}
const removeRecentSearchOnClick = (term) => {
const searchesWithoutThisOne = recentSearches.filter(search => search !== term)
setRecentSearches(searchesWithoutThisOne);
}
const dividendsYearsBackOnChange = (text) => {
setDividendsYearsBack(text);
}
const renderMainContent = () => {
if (!debouncedTerm) {
return (
<div className="ui active">
<div className="ui text">Search for info about a stock</div>
</div>
)
}
if (loading === true) {
return (
<div className="ui active dimmer">
<div className="ui big text loader">Loading</div>
</div>
)
}
if (errorMessage) {
return (
<div className="ui active">
<div className="ui text">{errorMessage}</div>
</div>
)
} else {
return (
<DividendResultsDisplay
data={dividendsData}
dividends_years_back={dividendsYearsBack}
dividendsYearsBackOnChange={dividendsYearsBackOnChange}
showMainInfo={showMainInfo}
showYieldChange={showYieldChange}
showAllDividends={showAllDividends}/>
)
}
}
// https://stackoverflow.com/questions/38619981/how-can-i-prevent-event-bubbling-in-nested-react-components-on-click
const renderRecentSearches = () => {
return recentSearches.map((term) => {
return (
<div key={term}>
<button
onClick={() => recentSearchOnClick(term)}
style={{marginRight: '10px'}}
>
<div>{term} </div>
</button>
<button
onClick={(event) => {event.stopPropagation(); removeRecentSearchOnClick(term)}}>
X
</button>
<br/><br/>
</div>
)
})
}
const renderSettingsView = (data) => {
if (settingsViewVisible) {
return (
<SettingsView data={data} />
)
} else {
return null;
}
}
const toggleSettingsView = () => {
setSettingsViewVisible(!settingsViewVisible);
}
const toggleDisplay = (e, setter) => {
setter(e.target.checked)
}
const SETTINGS_DATA = [
{
label: 'Main info',
id: 'main_info',
toggler: toggleDisplay,
setter: setShowMainInfo
},
{
label: 'Yield change',
id: 'yield_change',
toggler: toggleDisplay,
setter: setShowYieldChange
},
{
label: 'Dividends list',
id: 'all_dividends',
toggler: toggleDisplay,
setter: setShowAllDividends
},
]
console.log("showMainInfo: ", showMainInfo);
console.log("showYieldChange: ", showYieldChange);
console.log("showAllDividends: ", showAllDividends);
return (
<div className="ui container" style={{marginTop: '10px'}}>
<SearchBar term={term} onTermUpdate={onTermUpdate} />
{renderRecentSearches()}
<br/><br/>
<button onClick={toggleSettingsView}>Display settings</button>
{renderSettingsView(SETTINGS_DATA)}
<div className="ui segment">
{renderMainContent()}
</div>
</div>
)
}
const mapStateToProps = state => {
return { userId: state.auth.userId };
};
export default connect(
mapStateToProps
)(SearchPage);
// export default SearchPage;
the settingsView component:
import React from 'react';
import SettingsCheckbox from './SettingsCheckbox';
const SettingsView = ({data}) => {
const checkboxes = data.map((checkbox_info) => {
return (
<div key={checkbox_info.id}>
<SettingsCheckbox
id={checkbox_info.id}
label={checkbox_info.label}
toggler={checkbox_info.toggler}
setter={checkbox_info.setter}/>
<br/>
</div>
)
});
return (
<div className="ui segment">
{checkboxes}
</div>
);
}
export default SettingsView;
SettingsCheckbox.js:
import React, {useState} from 'react';
const SettingsCheckbox = ({id, label, toggler, setter}) => {
const [checked, setChecked] = useState(true)
return (
<div style={{width: '200px'}}>
<input
type="checkbox"
checked={checked}
id={id}
name={id}
value={id}
onChange={(e) => {
setChecked(!checked);
toggler(e, setter);
}} />
<label htmlFor="main_info">{label}</label><br/>
</div>
);
}
export default SettingsCheckbox;
import React from 'react' import { useState } from 'react'
const App = () => {
const [tasks, setTasks] = useState([
{ id: 1, text: 'Task1' }, { id: 2, text: 'Task2' }, { id: 3, text: 'Task3' }
])
const showTasks = tasks.map((task) => <h2>{task.text}<button onClick={() => onDelete(task.id)}>X</button></h2>)
const onDelete = (id) => {
setTasks(tasks.filter((task) => task.id !== id))
}
const [text, setText] = useState('')
const onSubmit = (e) => {
e.preventDefault()
if (!text || tasks > text || text === setTasks) {
alert('Problem');
return
}
addTask({ text })
setText('')
}
const addTask = (task) => {
const id = Math.floor(Math.random() * 10000) + 1;
const newTask = { id, ...task };
setTasks([...tasks, newTask]);
}
return (
<div>
<div>{showTasks}</div>
<form onSubmit={onSubmit}>
<input type='text' value={text} onChange={(e) => setText(e.target.value)} />
<input type='submit' value='Save Task' />
</form>
</div>
) }
export default App
Following is the condition to avoid duplicates in the onSubmit function.
!text || tasks.some((item) => item.text === text)
const App = () => {
const [tasks, setTasks] = React.useState([
{ id: 1, text: "Task1" },
{ id: 2, text: "Task2" },
{ id: 3, text: "Task3" }
]);
const showTasks = tasks.map((task) => (
<h2 key={task.id}>
{task.text}
<button onClick={() => onDelete(task.id)}>X</button>
</h2>
));
const onDelete = (id) => {
setTasks(tasks.filter((task) => task.id !== id));
};
const [text, setText] = React.useState("");
const onSubmit = (e) => {
e.preventDefault();
if (!text || tasks.some((item) => item.text === text)) {
alert("Duplicate");
return;
}
addTask({ text });
setText("");
};
const addTask = (task) => {
const id = Math.floor(Math.random() * 10000) + 1;
const newTask = { id, ...task };
setTasks([...tasks, newTask]);
};
return (
<div>
<div>{showTasks}</div>
<form onSubmit={onSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<input type="submit" value="Save Task" />
</form>
</div>
);
};
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
I am learning react and I reached a problem I can't get past.
On the upmost component I have a lot of functions that depend on state and that modify state. These will get passed on to children components and get linked to event handlers.
The file gets really large and I would like to somehow separate the functions and not clutter all of them in one file.
I created a demo here,you can see the App component gets really cluttered with functions.
What options do I have to separate the functions?
const {
useState,
useEffect,
useRef,
useCallback
} = React;
const useStateWithCallback = initialState => {
const [state, setState] = useState({
value: initialState,
callback: undefined
});
useEffect(() => {
if (state.callback) {
state.callback();
}
}, [state]);
const setStateWithCallback = (newValue, callback) => {
const value =
typeof newValue === "function" ? newValue(state.value) : newValue;
setState({
value,
callback
});
};
return [state.value, setStateWithCallback];
};
const Day = ({
input1,
input2,
handleInputChange,
isLocked,
index,
className
}) => {
return (
<div className={className}>
<input
name="input1"
value={input1}
placeholder="lorem"
onChange={handleInputChange}
readOnly={isLocked}
data-index={index}
/>
<input
name="input2"
value={input2}
placeholder="ipsum"
onChange={handleInputChange}
readOnly={isLocked}
data-index={index}
/>
</div>
);
};
const Menu = ({
handleSelectChange,
clearInputs,
submitInputs,
handleLock,
isLocked,
handleDateChange
}) => {
return (
<React.Fragment>
<select name="date" onChange={handleDateChange}>
<option value="01">01</option>
<option value="02">02</option>
<option value="03">03</option>
<option value="04">04</option>
<option value="05">05</option>
<option value="06">06</option>
</select>
<select name="word" onChange={handleSelectChange}>
<option value="lorem">Lorem</option>
<option value="ipsum">Ipsum</option>
</select>
<button onClick={clearInputs}>Clear</button>
<button onClick={submitInputs}>Submit</button>
{isLocked ? (
<button onClick={handleLock}>Unlock</button>
) : (
<button onClick={handleLock}>Lock</button>
)}
</React.Fragment>
);
};
const Month = ({
inputs1,
inputs2,
handleInputChange,
isLocked,
mobile
}) => {
return ( < React.Fragment >
<
Day input1 = {
inputs1[0]
}
input2 = {
inputs2[0]
}
handleInputChange = {
handleInputChange
}
isLocked = {
isLocked
}
index = {
0
}
className = {
mobile ? "mobile" : "day"
}
/> <
Day input1 = {
inputs1[1]
}
input2 = {
inputs2[1]
}
handleInputChange = {
handleInputChange
}
isLocked = {
isLocked
}
index = {
1
}
className = {
mobile ? "mobile" : "day"
}
/> < /React.Fragment >
);
};
const App =()=>{
const [inputs1, setInputs1] = useStateWithCallback(
Array.from({ length: 2 }, () => "")
);
const [inputs2, setInputs2] = useStateWithCallback(
Array.from({ length: 2 }, () => "")
);
const [word, setWord] = useState("?");
const [isLocked, setIsLocked] = useStateWithCallback(false);
const [mobile, setMobile] = useState(true);
const [date, setDate] = useState(new Date().getDate());
const handleSelectChange = event => {
setWord(event.target.value);
};
const handleInputChange = event => {
if (event.target.name === "input1") {
const newInputs1 = [...inputs1];
newInputs1[event.target.dataset.index] =
event.target.value.length === 3
? event.target.value + word
: event.target.value;
setInputs1(newInputs1);
} else if (event.target.name === "input2") {
const newInputs2 = [...inputs2];
newInputs2[event.target.dataset.index] =
event.target.value.length === 4
? event.target.value + word + "%%"
: event.target.value;
setInputs2(newInputs2);
}
};
const clearInputsOnServer = () => {
return new Promise(resolve => setTimeout(resolve, 800))
.catch(() => console.log(`couldn't clear`))
.then(console.log("succesfully cleared inputs on server"));
};
const clearInputs = () => {
setInputs1(
Array.from({ length: 2 }, () => ""),
setInputs2(Array.from({ length: 2 }, () => ""), clearInputsOnServer)
);
};
const submitInputs = () => {
return new Promise(resolve => setTimeout(resolve, 800))
.catch(() => console.log(`couldn't update`))
.then(console.log("submitted inputs to server"));
};
const updateLockOnServer = () => {
return new Promise(resolve => setTimeout(resolve, 800))
.catch(() => console.log(`couldn't update`))
.then(console.log("updated lock status on server"));
};
const handleLock = () => {
setIsLocked(wasLocked => !wasLocked, updateLockOnServer);
};
let timeout = useRef();
const handleResize = useCallback(() => {
clearTimeout(timeout.current);
timeout.current = setTimeout(() => {
const newMobile = window.innerWidth <= 600 ? true : false;
if (mobile !== newMobile) setMobile(newMobile);
}, 300);
}, [mobile]);
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [handleResize]);
const handleDateChange = event => {
setDate(event.target.value);
};
const getValuesFromServer = useRef(() => {
return new Promise(resolve => setTimeout(resolve, 800))
.then(console.log("succesfully updated inputs from server"))
.catch(() => {});
});
useEffect(() => {
getValuesFromServer.current();
}, [date]);
return (
<React.Fragment>
<Menu
handleSelectChange={handleSelectChange}
clearInputs={clearInputs}
submitInputs={submitInputs}
handleLock={handleLock}
isLocked={isLocked}
handleDateChange={handleDateChange}
/>
<Month
inputs1={inputs1}
inputs2={inputs2}
handleInputChange={handleInputChange}
isLocked={isLocked}
mobile={mobile}
/>
</React.Fragment>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render( <
React.StrictMode >
<
App/ >
<
/React.StrictMode>,
rootElement
);
.App {
font-family: sans-serif;
text-align: center;
}
.mobile input {
background-color: yellow;
font-size: 20px;
}
<div id="root"></div>
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
you can abstract scopes from your code into separate hooks folder than call into you App component extracting only what you need. for example all your lock state logic could be a hook like:
// useLockHandler.js at your hooks folder
const useLockHandler = () => {
const [isLocked, setIsLocked] = useStateWithCallback(false);
const updateLockOnServer = () => {
fetch(`server`, {
method: "put",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
isLocked
})
})
.catch(() => console.log(`couldn't update`))
.then(console.log("updated lock status on server"));
};
const handleLock = () => {
setIsLocked(wasLocked => !wasLocked, updateLockOnServer);
};
// expose what you need at your component.you can return as array or object
return { isLocked, handleLock }
}
export default useLockHandler
than at your App you would import useLockHandler and extract the variables you need:
export default function App() {
// all other states setting
const { isLocked, handleLock } = useLockHandler()
Guys I need help to make an API search request that should be done when page is ready.
This is state object:
const [state, setState] = useState({
s: "",
results: [],
selected: {}
});
const apiurl = ....;
This is how my search input actually works:
const search = (e) => {
if (e.key === "Enter") {
axios(apiurl + "&s=" + state.s).then(({data}) => {
let results = data.Search;
setState(prevState => {
return {...prevState, results: results }
});
});
}
}
const handleInput = (e) => {
let s = e.target.value;
setState(prevState => {
return { ...prevState, s: s }
});
}
My components:
return (
<div className='basic'>
<Header />
<Search handleInput={handleInput} search={search} />
<Container>
<Results results={state.results} openPopup={openPopup} />
{(typeof state.selected.Title != "undefined") ? <Popup selected={state.selected} closePopup={closePopup} /> : false }
</Container>
</div>
);
Search.js:
function Search ({ handleInput, search})
return (
<Container bg="dark" variant="dark">
<section className="searchbox-wrap">
<input
type="text"
placeholder="Поиск фильма"
className="searchbox"
onChange={handleInput}
onKeyPress={search}
/>
</section>
</Container>
)
Results.js:
function Results ({ results, openPopup }) {
return (
<section className="results">
{results.map(result => (
<Result key={result.imdbID} result={result} openPopup={openPopup} />
))}
</section>
);
}
So how can I make search request (for example: Superman) be done when page is loaded? Thank you!
You can do that with useEffect hook, that is equivalent to componentDidMount() lifecycle method when an empty array is passed as a second argument. So modified code would look like the following:
import React, { useState, useEffect, useCallback } from 'react';
function SearchComponent() {
const [state, setState] = useState({
s: "Superman",
results: [],
selected: {}
});
const apiurl = "";
const makeSearchRequest = useCallback(
searchString => {
axios(apiurl + "&s=" + searchString)
.then(({ data }) => data.Search)
.then(results => setState(prevState => ({ ...prevState, results })));
},
[setState]
);
// This will be invoked only on component mount
useEffect(() => makeSearchRequest(state.s), []);
const handleInput = useCallback(
e => {
e.persist();
setState(prevState => ({ ...prevState, s: e.target.value }));
},
[setState]
);
const search = useCallback(
e => {
if (e.key === "Enter") {
makeSearchRequest(state.s);
}
},
[makeSearchRequest, state.s]
);
return (
<input
type="text"
value={state.s}
onChange={handleInput}
onKeyPress={search}
/>
);
}