I recently posted a question with regards to sending and receiving data via a websocket. In the interest of not duplicating most of the work, the original question is found here: Socket.io emitting undefined variable React
I have since modified my app slightly so that each id received from the server gets added or removed to a disabled array (if the focus parameter received from the server is true, add the id and if not, remove it). This is so that I can disabled/enable questions based off the focus sent.
The slight adjustments I've made are three fold, all within the App.js file
1. Add disabled array to state
const [disabled, setDisabled] = useState([])
2. Include disabled parameter to the question
<TextArea
cols={50}
helperText="Optional helper text here; if message is more than one line text should wrap (~100 character count maximum)"
id="text2"
invalidText="Invalid error message."
labelText="Text area label"
placeholder="Placeholder text"
rows={4}
onFocus={() => setFocusTrue('text2')}
onBlur={() => setFocusFalse('text2')}
disabled={disabled.indexOf('text2')!==-1}
/>
You will note in the above that the disabled parameter will be set to true if the questionId is included in the disabled array (therefore disabling the question) and false if not
3. Add questionId to disabled state array
useEffect(() => {
socket.on("message", ({ user, id, focus }) => {
console.log(user, "clicked on", id, "with focus", focus)
console.log('adding', id, 'to disabled')
setDisabled(prevDisabled => [...prevDisabled, id]);
console.log("disabled:", disabled)
})
}, [])
For now, I am just adding all the questionIds coming from the server to the disabled array so I can test whether they are indeed getting added. But this is my issue in that the disabled array is always empty (see screenshot below). The id variable exists as can be seen in the console log output that precedes setting the new disabled state, but it is not getting added as expected.
EDIT:
As per the comments, I can see that the disabled array is in fact getting updated, if I console.log(disabled) just before rendering. However, if I apply any sort of logic to the array, I get an error. The below is the modified useEffect which contains some logic (essentially, to add or remove an id to the array)
useEffect(() => {
socket.on("message", ({ user, id, focus }) => {
console.log(user, "clicked on", id, "with focus", focus)
console.log('adding', id, 'to disabled')
if (focus) {
console.log('not my user and focus is true')
setDisabled(prevDisabled => [...prevDisabled, id])
console.log("disabled after adding", disabled)
} else {
let filteredArray = disabled.filter(idToRemove => idToRemove !== id)
setDisabled({disabled: filteredArray});
}
console.log("disabled:", disabled)
})
}, [])
When I click on a textbox, the id of the question gets added to the disabled array as expected, but when I click out of it, I get the following error:
disabled.indexOf is not a function
This referring to disabled={disabled.indexOf('text1')!==-1}
I think your code is fine, if you are trying to console.log the most resent state right after you setting it, it won't work why? setState is asynchronous it might not work real time as you expected.
what you actually want to try is add a useEffect and listen for changes of the disabled state
useEffect(() => {
console.log("disabled", disabled);
}, [disabled]); // since disabled is in the dependency array this hook function will call, every time when the disabled gets updated.
// so you are so sure the disabled is getting updated correctly
or just do a simple console.log(disabled) right before the render and see.
and your modified version of useEffect is incorrect as I see, it should be corrected as
....
const filteredArray = disabled.filter(idToRemove => idToRemove !== id)
setDisabled(filteredArray);
....
Related
I have an event app with a signup form with a list of checkboxes that contain reasons as to why the user wants to sign up for events. On our events page we have a similar signup form, however when signing up on the events page it's already implied they want to attend events, so I want to always pass the "I want to attend events" checkbox value when they signup. Currently, I have it set to it's checked every by default but I'm confused on how to also send that value by default, storing these values in 'reasons'. When submit is clicked, nothing populates for reasons but I always need at least one reason each time
const [reasons, setReasons] = useState([])
what happens when a checkbox is checked
const onReasonChange = event => {
// Remove the reason if already selected
if (reasons.includes(event.target.value)) {
setReasons(reasons.filter(reason => reason !== event.target.value))
return
}
// Add the reason
setReasons([...reasons, event.target.value])
}
the checkbox i'm trying to get to always send the value of
<input
id="attend"
type="checkbox"
value="I want to attend events"
onChange={onReasonChange}
checked={reasons.includes("I want to attend events"), 'checked'}
/>
The simplest way to include the default value in your state and have the checkbox be checked is to initialise the state with the default value.
Your state could also work better as a Set if order isn't important
// Saves you repeating string literals
const attendReason = "I want to attend events";
const [reasons, setReasons] = useState(new Set([attendReason]));
const onReasonChange = ({ target: { checked, value } }) => {
setReasons((prev) => {
prev[checked ? "add" : "delete"](value);
return new Set(prev);
});
};
<input
name="attend"
type="checkbox"
value={attendReason}
checked={reasons.has(attendReason)}
onChange={onReasonChange}
/>
I am making a search functionality into react that effectively looks for data from json-server for a match. I don't want to provide a debounced search to the input field, rather I want to trigger the search when "Enter" key is pressed. So i used onKeyPress prop from MUI's textfield, where I provided the logic to send the query to the server.
Please acknowledge my code as mentioned below -
imports...
export default function AppSearchBar ( ) {
// local state for searchTerm
const [ searchTerm, setSearchTerm ] = useState<string>('');
// using redux - action
const {loadedBooks} = useAppSelector(state => state.books);
const {loadedGames} = useAppSelector(state => state.games);
// these 'loadedBooks' and 'loadedGames' are of type boolean and are initially false (coming from my slices) and set to true when their requests are getting fulfilled.
const dispatch = useAppDispatch();
// useCallback
const fetchAllCategories = useCallback(() => {
setTimeout( async () => {
await dispatch(fetchBooksAsync( searchTerm )); // this is async thunks i created to fetch books into my bookSlice.ts file
await dispatch(fetchGamesAsync( searchTerm )); // this is async thunks i created to fetch books into my gameSlice.ts file
}, 500);
}, [ searchTerm , dispatch ]);
// effect when searchTerm mounts
/* useEffect(() => {
fetchAllCategories()
}, [ fetchAllCategories ]); */ // dependency as the function itself.
// I want this useEffect, but un-commenting this is not allowing my "handleSearchOnEnter" to come into the picture at all, but, I want that first load of all cars be automatic, and then when I write something to input, on pressing enter it should search, and finally when I wipe input field, it should return me back all the cards.
const handleSearchOnEnter = ( event : any ) => {
if ( event.key === "Enter" ) {
fetchAllCategories(); // this is wrapped inside of useCallBack and effect is produced using useEffect.
}}
return (
<Fragment>
<TextField
value = {searchTerm}
onChange = {( event : any ) => setSearchTerm(event.target.value)}
onKeyPress = { searchTerm !== "" ? handleSearchOnEnter : undefined } />
</Fragment>
)
}
Now, problem statement -
Whenever I load my window, all Books and Games are not loaded at all (if I remove useEffect() from code). They only loads when I press enter. But, I don't want this behaviour.
If I keep useEffect() - hook, then they behaves like debounce search and onChange of my text input field, they return the searched result.
What I want is as follows -
- Whenever I first loads the window, all products get loaded.
- Whenever I write something into the input field, then it shouldn't call (fetchFiltersAsync() - which is the API call for full-text search on Json-Server) until i press Enter key, only When I press enter, it should call the API and fetch the relevant results.
- After that, when I manually remove the searchedItem from input field (wiping it), all of my data should get returned. (i.e display all cards into UI)
What is Happening? -
Whenever My window loads, all of my data/cards are not getting loaded., until I presses enter key...cool
When I type something into input field, it fetches the searched results without even taking "Enter" (because of open useEffect() into the code)
When I remove a term from the input field, my data is not getting loaded automatically back to like as they were into first visit (all cards visible).
All controls are here only (into the code), I have to do something with searchTerm, so whenever searchTerm is not empty, then my handleSearchOnEnter() function should get called.
I mean, it should produce useEffect automatically, but only when a searchTerm is not being provided. If the searchTerm is being provided, then it should trigger handleOnEnterSearch()
I had the same issue that is described in the second Problem I solved it by adding in my project.
<form onSubmit={onKeyDownHandler}>
<TextField>{"Some Code"}</TextField>
</form>;
Also you can create an useState and giving it new Date will refresh your table better.
const onKeyDownHandler = (e) => {
e.preventDefault();
// if (searchTxt.length >= 3 || !searchTxt.length) {
dispatch(setSearchTxt(searchTxtTemp));
setTriggerSearch(new Date().getTime());
// }
};
But the bad sides of this code is when you remove everything from input you need to press enter again to refresh.
I got simple blog with arficles, and when user click edit button he got form filled with articles data - title, description, body and tags. I use useEffect to get data and fill form, when I got "id". If there is no "id" form should be blank. here is my useEffect:
useEffect(() => {
if (id) {
isLoading = true;
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}, [id]
);
but when I reload page id not changed, and func userService.articles.get(id) not run, and all datas gone. I need advice how to fix it? may be I need to use other deps for useEffect, but now I have no idea what deps i can use exept id.
upd:
thank you all for help. all i want is:
when the edit page load/reload and "id" exist -> fills form fields
when "id" not exist -> blank form fields
now when I reload edit page i got id - but all datas gone, and i got blank form :(
Here is the full code: codesandbox
p.s. i use free API - so you can create user in a sec with any imagined email, username and password. you don't need mail confirmation.
You should use this.props.match.params to access your id that comes from the url.
useEffect(() => {
if (props.match.params.id) {
setIsloading(true);
userService.articles.get(props.match.params.id)
.then((resp) => {
setIsloading(false);
props.onLoad(resp)
})
} else {
props.onLoad(null);
}
}, [props.match.params.id]);
Also you should rely on useState to manage your isLoading variable.
const [isLoading, setIsloading] = useState(false);
I did a bit more digging into the code you have provided.
The initialValues will be first empty because the data coming from the props is not there yet. And once the initialValues have been set you can't change them dynamically, you have to resort to the antd Form api.
You cannot set value for each form control via value or defaultValue
prop, you should set default value with initialValues of Form. Note that initialValues cannot be updated by setState dynamically, you
should use setFieldsValue in that situation.
The key here is to use another useEffect with dependencies to your form values comming from the props and use those to reset the form values via setFieldsValue.
try to useEffect without options and it will run just when the page loads for the first time
useEffect(() => {
if (id) {
isLoading = true;
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}, []
);
Based on the assumption that you want props.onLoad to run whenever there is a defined "id" or the defined "id" changes:
Returning a function from a useEffect hook (as you do with return props.onLoad(...)) is specifically for "cleaning up" things like side effects or subscriptions. A function returned inside a useEffect hook will only run when the component unmounts. See docs here. Also it doesn't seem like you are even passing a function to run on cleanup. You're passing the result of props.onLoad to run on cleanup, which based on the function name doesn't seem like it is intended to return another function.
So, if you want props.onLoad() to run if the "id" is defined, remove the return before props.onLoad. That return is telling React to hold (what it thinks is a function) for cleanup on unmount. If it's still not working, I think we'll need more information on what exactly props.onLoad is doing.
I am trying to achieve a particular scenario by writing an epic in redux-observable.
Elements in View
A dropdown: Dropdown has countries,
A Text box : has the user name field
A Checkbox : user selection of a persona(similar to admin vs standard)
Redux State stores the following
Country Dropdown value
Text Box value and status(disabled or enabled)
Checkbox state
Assumption
If the text box is disabled, it means the text box already has some value.
I need to call an api(accepts the country and username as input) whenever the user changes the value in the dropdown, or if he un-checks the checkbox(when value is false) or if the text box is disabled.
export default (action$: Observable<any>, state$: State$) => {
const countryValue$ =
state$.pipe(
map(o => o.countryCode),
distinctUntilChanged(),
filter(Boolean),
);
const email$ =
state$.pipe(
filter(o => o.isTextBoxLocked),
filter(o => o.isAdmin === false),
map(o => email),
distinctUntilChanged(),
);
return combineLatest(countryValue$, email$)
.pipe(map(([country, email]) =>
actions.fetchData(country, email)));
};
The problem is, if the checkbox value is true, still the api call is triggered(because it picks the last value emitted by the email stream). How do I fix this? This is happening because of my usage of combineLatest.
There are four ways to subscribe for multiple streams in rxjs.
CombineLatest, Zip, WithLatestFrom, ForkJoin
All serve its own purpose. For more details,
https://scotch.io/tutorials/rxjs-operators-for-dummies-forkjoin-zip-combinelatest-withlatestfrom
Your problem seems to be different and not based on the operator, the request is getting fired for changes in checkbox though you don't have a subscriber written to it. But its part of the state. You might have to check for filter condition and the key distinctUntilChanged() served its purpose or not.
I have a button that submits selected values to api. Once this has been submitted I am then trying to turn button state to disable and rest the values selected back to original state before nay where selected.
This is what I am doing on upload handle:
handleStatusEditsUpload = () => {
const { value, status } = this.state;
this.setState({
value: selected,
status: {}
});
};
In my real version locally status is clearing, status is used when changing all values at the same time by clicking the header title, a dialog appears to change all values in that column.
The main one I am having trouble is with the value. Value is populated with a new array that looks at table cell and row.
Here is demo to my project: https://codesandbox.io/s/50pl0jy3xk
Why isnt the state changing? any help appreciated as always.
What is happening is that you are mutating state in your "handleValue" method.
const newValue = [...this.state.value]; // this holds reference
newValue[rowIdx][cellIdx] = val; // so that here your state is mutated ( and const "selected" with it)
In the long term you probably should change your data structure a bit, so it would be easier to merge updates in to your state value. But a quick fix would be to clone the state value before mutating it:
handleValue = (event, val, rowIdx, cellIdx) => {
const newValue = _.cloneDeep(this.state.value); // no reference anymore
newValue[rowIdx][cellIdx] = val; // update the cloned value
this.setState({
value: newValue
});
};
I just ran your code in the sandbox you provided and it's throwing errors when you click the confirm button (trying to spread non-iterable). Once that is corrected, the state updates correctly. See my fork below:
https://codesandbox.io/s/385y99575m
I've left in a few console logs so you can see the component state updating when your onClick fires.
Why are you passing in your props to the handleStatusEditsUpload method? It doesn't take an argument. Was this just part of your debugging process?