I'm trying to convert the following setState function to React Hooks
const setUserAnswer = (answer) => {
this.setState((state) => ({
answersCount: {
...state.answersCount,
[answer]: (state.answersCount[answer] || 0) + 1
},
answer: answer
}));
Hope it will help:
const [answersCount, setAnswersCount] = useState(<default_value>);
const [answer, setAnswer] = useState(<default_value>);
const setUserAnswer = (answer) => {
setAnswersCount(previousState => {
return {
...previousState,
[answer]: (previousState[answer] || 0) + 1
}
})
setAnswer(answer)
}
Related
I'm new to the whole React and React hooks staff, I'm trying to reset the value in useState to default if new filters are selected.
const [apartments, setApartments] = React.useState([])
const [page, setPage] = React.useState(1)
const fetchData = (onInit = true, loadMore = true) => {
let setQuery = ''
if (!loadMore) {
setApartments([]) // <--- how to reset to empty?
setPage(1) // <--- Not setting value to 1
}
if (!onInit || query()) {
filtersWrapper.current.querySelectorAll('.select-wrapper select').forEach(item => {
if (item.value) {
setQuery += `&${ item.name }=${ item.value }`
}
})
}
fetch(apiUrl + `?page=${ page }&pageSize=12${ setQuery }`)
.then(res => res.json())
.then(data => {
setApartments([...apartments, ...data.apartments] || [])
setHasNextPage(data.hasNextPage)
setPage(prev => prev + 1)
})
}
Identify the page to pass to fetch from the loadMore argument, not from the state value. Similarly, identify from the argument what to pass to setApartments. This way, all the state gets updated at once, inside the fetch.
const fetchData = (onInit = true, loadMore = true) => {
let setQuery = ''
if (!onInit || query()) {
filtersWrapper.current.querySelectorAll('.select-wrapper select').forEach(item => {
if (item.value) {
setQuery += `&${item.name}=${item.value}`
}
})
}
const pageToNavigateTo = loadMore ? page : 1;
fetch(apiUrl + `?page=${pageToNavigateTo}&pageSize=12${setQuery}`)
.then(res => res.json())
.then(data => {
const initialApartments = loadMore ? apartments : [];
setApartments([...initialApartments, ...data.apartments]);
setHasNextPage(data.hasNextPage);
setPage(pageToNavigateTo + 1);
})
// .catch(handleErrors); // don't forget this
}
I'd also recommend changing the
filtersWrapper.current.querySelectorAll('.select-wrapper select').forEach
from DOM methods to setting React state instead.
i'm new to react hooks, here i have been converting my project to hooks from classes, i'm getting this kind of message 'Error: Server error
at build_error (actions.js:57)
at eval (actions.js:83)' and 'GET http://127.0.0.1:8000/api/kamera/undefined 404 (Not Found)'
those errors come when i'm changing class to hooks (everything is set correcly using useState and useEffect), any idea ?
class:
initializeCollapses() {
const data = this.props[this.props.action];
let collapseStates = this.state.collapseStates;
if (!data || data.length < 1) {
return;
}
data.map((el) => {
collapseStates["" + el.name + el.identifier] = false;
return;
});
this.setState({
...this.state,
collapseStates: collapseStates,
});
}
componentDidMount() {
this.props.getItems[this.props.action](this.state.actionArgs).then(() => {
this.initializeCollapses();
});
}
Hooks:
const initializeCollapses = () => {
const data = [action];
if (!data || data.length < 1) {
return;
}
data.map((el) => {
collapseStates["" + el.name + el.identifier] = false;
return;
});
setCollapseStates(collapseStates);
};
useEffect(() => {
getItems[action](actionArgs).then(() => {
initializeCollapses();
});
}, []);
initializeCollapses() {
const data = this.props[this.props.action];
let collapseStates = this.state.collapseStates;
if (!data || data.length < 1) {
return;
}
data.map((el) => {
collapseStates["" + el.name + el.identifier] = false;
return;
});
this.setState({
...this.state,
collapseStates: collapseStates,
});
}
componentDidMount() {
this.props.getItems[this.props.action](this.state.actionArgs).then(() => {
this.initializeCollapses();
});
}
const mapDispatchToProps = (dispatch) => {
return {
getItems: {
analysers: (site) => dispatch(getAnalysers(site)),
platforms: (site) => dispatch(getPlatforms(site)),
brokers: (site) => dispatch(getBrokers(site)),
cameras: (site) => dispatch(getCameras(site)),
sites: (site) => dispatch(getSites())
},
};
};
The above class implementation in hooks would roughly be as below
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import getItems from "./store/actions";
or
import { cameras, sites, platform, brokers } from "./store/actions";
const actionArgs = useSelector(state => state.actionArgs); // In place of mapStateToProps
const dispatch = useDispatch();
useEffect(() => {
dispatch(getItems.cameras(actionArgs)) or dispatch(cameras(actionArgs)) //If destructured
}, []);
I have provided an understandable example with whatever data you provided. Refer this for a completely different approach or this one for the same mapDispatchToProps approach.
Good to refer
Example:
import React, {useReducer} from 'react';
const init = 0;
const myReducer = (state, action) => {
switch(action.type){
case 'increment':
return state + 1 // complex actions are kept in seperate files for better organised, clean code
case 'decrement':
return state - 1
case 'reset': // action types as well are kept as selectors
return init
default:
return state
}
};
function ReducerExample(){
const [count, dispatch] = useReducer(myReducer, init)
const add = () => {
dispatch({type: 'increment'})
}
const sub = () => {
dispatch({type: 'decrement'})
}
const reset = () => {
dispatch({type: 'reset'})
}
return (
<div>
<h4>Count: {count}</h4>
<button onClick={add} style={{margin: '10px'}}>Increment</button>
<button onClick={sub}>Decrement</button>
<button onClick={reset} style={{margin: '10px'}}>Reset</button>
</div>
)
}
export default ReducerExample;
I'm making MBTI app with ReactJS
But I have some problem now
When I click button i got some string ex 'E or I'
and then When it finished I got String value ex'EEINNSTTFPPJ'
so I want to change this value to 'ENTP'
How Can I make it ? 1.state
const TOTAL_SLIDES = 12
const [score, setScore] = useState(0)
const [type, setType] = useState([])
const [num, setNum] = useState(0)
const [currentSlide, setCurrentSlide] = useState(1)
const slideRef = createRef(null)
const history = useHistory()
const [mbti, setMbti] = useState('')
2.funtion
const nextSlideFir = () => {
setNum(num + 1)
setType(questions[num].answers[0].type)
setMbti(mbti + type)
setCurrentSlide(currentSlide + 1)
slideRef.current.style.transform += 'translateX(-100vw)'
}
const nextSlideSec = () => {
setNum(num + 1)
setType(questions[num].answers[1].type)
setMbti(mbti + type)
setCurrentSlide(currentSlide + 1)
slideRef.current.style.transform += 'translateX(-100vw)'
}
//I Don't know how to get same duplicate values
const mbitChecker = string => {
const words = [string]
return words.filter((item, index) => words.indexOf(item) !== index)
}
useEffect(() => {
currentSlide > TOTAL_SLIDES &&
mbitChecker(mbti) &&
history.push(`/result/${mbti}`)
})
)
Not sure what you are trying to, but you have better to have useEffect's dependency array
const extractDuplicates = (text) => {
// extractDuplicates('EEINNSTTFPPJ') -> "ENTP"
const ans = []
for (t of text) {
if (text.indexOf(t) !== text.lastIndexOf(t)) {
if (ans.indexOf(t) < 0) {
ans.push(t)
}
}
}
return ans.join('')
}
useEffect(() => {
currentSlide > TOTAL_SLIDES && history.push(`/result/${mbti}`)
console.log(`${mbti}`)
mbitChecker()
}, [mbti])
I need to convert my Class Components code to Functional Hooks Components. I have the Class Components logic below that works. However for me to implement Context Api I need to convert it to hooks.
I get storeList undefined when I console log it and this error...
TypeError: undefined is not an object (evaluating 'storeList'.map).
Is there a way I can make introduce a state inside a UseEffect? Thank you in advance
const { data, status } = useQuery('stores', fetchStores)
const [isSelected, setIsSelected] = useState(false)
const storeList = data
useEffect(() => {
let array = storeList.map((item, index) => {
isSelected = false
return { ...item }
})
setIsSelected({ ...isSelected, array: { ...storeList.array } })
selectHandler()
}, [])
const selectHandler = (ind) => {
let array = storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
setIsSelected({ ...isSelected, array: { ...storeList.array } })
}
Here is the same code as Class component and it works perfectly
async componentDidMount() {
let array = this.state.storeList.map((item, index) => {
this.isSelected = false
return { ...item }
})
this.setState({ storeList: array })
}
selectionHandler = (ind) => {
const { storeList } = this.state
let array = storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
this.setState({ storeList: array })
}
Try This:
const { data, status } = useQuery('stores', fetchStores)
const [isSelected, setIsSelected] = useState(false)
const storeList = data
useEffect(() => {
let array = storeList.map((item, index) => {
isSelected = false
return { ...item }
})
setIsSelected(state => ({ ...state, array: { ...storeList.array } }));
selectHandler()
}, [])
const selectHandler = (ind) => {
let array = storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
setIsSelected(state => ({ ...state, array: { ...storeList.array } }));
}
Reference: https://stackoverflow.com/a/63522873/10994570
This worked!! I hope it can help someone else. Thanks
const { data, status } = useQuery('stores', fetchStores)
const [isSelected, setIsSelected] = useState(false)
const storeList = data
useEffect(() => {
handlerControl()
}, [])
const handlerControl = async () => {
try {
let array = await storeList.map((item, index) => {
isSelected = false
return { ...item }
})
setIsSelected(array)
} catch (e) {
console.log(e)
}
}
const selectHandler = async (ind) => {
//alert('pressed')
try {
let array = await storeList.map((item, index) => {
if (ind == index) {
item.isSelected = !item.isSelected
}
return { ...item }
})
setIsSelected(array)
console.log(index)
} catch (e) {
console.log(e)
}
}
Is it an acceptable practice to have two custom react hooks in the same component, one after another?
The issue I am dealing with is as follows:
The first custom hook useBudgetItems will load, but the subsequent one will be undefined. I think I understand why it's happening (my budgetSettings property inside my useBudgetSettings loads after the console.log() statement), but I am not sure how to get around this and whether this is the right approach.
const BudgetCost ({ projectId }) => {
const { budgetCost, loaded } = useBudgetCost({ key: projectId });
const { budgetSettings } = useBudgetSettings({ key: projectId });
const [totalBudget, setTotalBudget] = useState(budgetCost.totalBudget);
const [budgetCosts, setbudgetCosts] = useState(budgetCost.items);
// This will be undefined
console.log(budgetSettings)
if(!loaded) return <div/>
return (
...
...
)
});
My useBudgetCost custom hook is as follow (the useBudgetSettings isn't much different in the mechanics.
const useBudgetCost = ({ key, notifyOnChange }) => {
const [loaded, setIsLoaded] = useState(false)
const { budgetCost, setBudgetCost } = useContext(ProjectContext)
useEffect(() => {
if(key)
return getBudgetCost(key);
},[key]);
const getBudgetCost = (key) => {
let { budgetCosts, loaded } = UseBudgetCostsQuery(key);
setBudgetCost(budgetCosts);
setIsLoaded(loaded);
}
let onBudgetCostChange = (update) => {
let tempBudgetCostItems = copyArrayReference(budgetCost);
tempBudgetCostItems = {
...tempBudgetCostItems,
...update
}
setBudgetCost(tempBudgetCostItems)
if (notifyOnChange)
notifyOnChange(update)
}
return {
loaded,
budgetCost,
onBudgetCostChange
}
}
useBudgetSettings component:
const useBudgetSetting = ({ key }) => {
const [loaded, setIsLoaded] = useState(false)
const { budgetSettings, setBudgetSettings } = useContext(ProjectCondext)
const globalContext = useContext(GlobalReferenceContext);
useEffect(() => {
if(key)
return getBudgetSetting(key);
},[key]);
const getBudgetSetting = (key) => {
let { budgetSettings, loaded } = UseBudgetSettingsQuery(key);
console.log(budgetSettings);
setBudgetSettings(budgetSettings);
setIsLoaded(loaded);
}
const getBudgetReferences = (overrideWithGlobal = false) => {
if(overrideWithGlobal)
return globalContext.getBudgetReferences();
return budgetSettings.map((item) => { return { value: item.key, label: item.costCode } });
}
const getCategoryText = (key) => _.get(_.find(getBudgetReferences(), (bc) => bc.value === key), 'label');
return {
loaded,
budgetSettings,
getCategoryText,
getBudgetReferences
}
}