I am trying to set an array to a state hook. Basically I want to keep a track of a per-row (of grid sort of) Edit Dialog Open State. Basically per row, I have a Edit button, launches a . As all seems rendered initially, I am trying to manage the show hide by keeping an array in the parent grid component. When user clicks on the Edit button, per row, I want to pass the rowData as props.data and want to provide the Edit functionality.
To keep the state of the editDialogs (show/hide), I am making a array of objects useState hook as follows:
const [editDialogsModalState, setEditDialogsModalState] = useState([{}]); // every edit dialog has it's own state
...
function initializeEditDialogsModalState(dataSet) {
let newState = [];
dataSet.map((item) => newState.push({ id: item.id, state: false }));
return setEditDialogsModalState(newState); // **PROBLEM->Not setting**
}
function addUDButtons(currentRowDataMovie) { // my edit/delete button UI code
const currRowDataId = currentRowDataMovie.id;
return (
<span>
<button
type="button"
className="btn btn-info"
onClick={() => setEditDialogsState(currRowDataId)}
>
Edit
</button>
{editDialogsModalState[currRowDataId].state && ( // **PROBLEM->null data even after set call**
<EditMovieComponent
open={editDialogsModalState[currRowDataId].state}
onToggle={toggleEditDialogsModalState(currentRowDataMovie)}
movie={currentRowDataMovie}
/>
)}
}
......
function buildGrid() {
{
if (!ready) {
// data is not there, why to build the grid
return;
}
initializeEditDialogsModalState(movies);
...........
}
However not able to get the editStates. A screen shot from debugger where I can see the movies (REST output), ready, but not the editDialogsModalState state array.
In general, is there a better ways of implementing such per-row basis functionality where on click of a button I want to open a React-bootstrap and pass the row-specific dataitem for doing operations ? (I am learning React, so may not not yet fully aware of all pointers).
Thanks,
Pradip
Related
Having a hard time seeing how I could accomplish this. I created some custom number buttons from 0-9 that users can click on instead of using the keyboard. The problem I'm having is I have multiple dynamically created input fields depending on JSON Data so let's say there are 10 dynamically created input fields and a user starts with question one and the user then uses the custom number buttons I created and clicks numbers "145" to answer question one, but what happens is then all 10 inputs have the same number "145" not the problem the user was trying to solve. I'm using the context API to then save the values typed in on a function called getButtonValue that I then call to the parent component and save the values in a state array, so I know that my problem is that all the inputs share the same state array but how could I make sure the correct input the user clicks on is only receiving those values.
Thanks in advance.
My Custom Number Button Component:
import { FormContext } from "../../lib/FormContext";
function ActivityBar() {
const { getButtonValue } = useContext(FormContext);
return (
<div className={`${activity.activity__workSheet__numberButton}`}>
<button value={0} onFocus={(e) => getButtonValue(e)}>
<img
className={`${activity.activity__workSheet__img0}`}
src={"/assets/activityNumber-btn.png"}
alt="activity number button"
/>
.... more code
Parent Component:
const [numberButtonClicked, setNumberButtonClicked] = useState([]);
const getButtonValue = (e) => {
setNumberButtonClicked((prevButtonClicked) => [
...prevButtonClicked,
e?.target?.attributes[0].value
]);
};
return (
<Carousel>
<div ref={imageRef} style={{ height: "100%" }}>
{Object.entries(elements).map((element, i) => {
const { fields } = element[1];
if (fields) {
return (
<Element
key={i}
field={fields[0]}
id={i}
useReff={`answer${i}`}
currentValue={
numberButtonClicked === "" ? null : numberButtonClicked.join("")
}
/>
);
} else {
return;
}
})}
</div>
</Carousel>
Got a good working version figured out for this scenario, what I did was.
I have a onFocus method on my input tags that then takes in the event and calls a handleChange(e) function. Within that function I then save the currentInputId in a variable by using e?.target?.attributes[0]?.value and the previous InputId in a state variable and just check if the previous InputId is equal to the currentId user just focused on. If so then we'll add the next number user clicks into the same field, else if previousInputId !== currentInputId then make my user value state array empty, setNumberButtonClicked([]).
I'm currently using Material-table . It displays data normally however, Pagination and Row per Page dropdown is not working. Nothing happens upon clicking, next button and selected number of rows.
See below codes:
import MaterialTable from 'material-table'
const tableIcons = {
/*table icons*/
}
function Test(){
const [data, setData] = useState([]);
const getDatas = async() => {
await axios.get('/API')
.then(response => {
setData(response.data)
}
}
const columns = [
{.....} //columns
]
return(
<div>
<MaterialTable
icons = {tableIcons}
columns = {columns}
data = {data}
title = 'List of data'
actions = {[{
//add button properties
}]}
>
</MaterialTable>
</div>
)
}
export default Test;
I'm getting the following error on console upon onload and clicking pagination buttons.
On load:
On click of next button
Please help me with this. Thank you in advance.
First of all, keep in mind that the original project was discontinued, and the new direction can be found in this repository (it's a fork of the original). There will be a lot of refactorings and breaking changes, so you might want to check them out first.
Now, on your question,
since you are working with remote data you could check out the official example on how to handle this kind of data.
If your requirements don't allow you to do this, you will need to do all the handling by yourself. That means you should provide your own implementation of the Pagination component, in which you define your own behavior of onChangePage and other callbacks.
The customisation will look something like:
Pagination: (properties: any) => {
return (
<TablePagination
{...properties}
count={currentPage.total}
onChangePage={(event: any, page: number) => {
onChangePage(page);
}}
page={currentPage.startIndex / pageSize}
/>
);
}
where total, startIndex etc. will be provided by the API you consume, along with the actual data that you show in the table.
These components overrides should be provided under the components property of the material table.
In a complex tabbed form in react-admin I need to have two submit buttons, one is the regular save button and one for altering the "status" field (advancing one workflow step) and saving the form.
The save butten should only become active if all required fields are filled by the user.
The other button changes its text depending on a "status" field in the record which contains the current workflow step, and is only active when the form validation for the current workflow step passes.
So either I need a dynamic button or several buttons which show and hide depending on the "status" field.
I think the dynamic button would be the more elegant solution.
Below you see the code I currently have, it is more or less copied from the react-admin documentation. I need to add a custom save button as well, but it is just a subset, easy to do when the AdvanceWorkflowButton works at the end.
const AdvanceWorkflowButton= ({ handleSubmitWithRedirect, ...props }) => {
const [create] = useCreate('posts');
const redirectTo = useRedirect();
const notify = useNotify();
const { basePath, redirect } = props;
const form = useForm();
// I need to set the label dynamically ... how?
// I also need sth like:
// if (validationSucceeds()) enable=true
const handleClick = useCallback(() => {
// here I need to check the current content of the "status" field.... how?
form.change('status', { "id": 2, "name": "Vorbereitung begonnen" });
handleSubmitWithRedirect('list');
}, [form]);
return <SaveButton {...props} handleSubmitWithRedirect={handleClick} />;
};
const CustomToolbar = props => (
<Toolbar {...props} >
<SaveButton
label="Speichern"
redirect="list"
submitOnEnter={true}
variant="text"
/>
<AdvanceWorkflowButton />
</Toolbar>
);
I had the exact same trouble.
Needed a button to save the form without validation, and another to save and change status with validation in place.
The code above helped me get to the answer, here are my configuration of the components necessary to achieve the desired outcome.
Set a new truthy value up in the form data as follows when the user clicks the save and next. Check the new property ('goNextStep' in our example) on the server to move the process forward.
<SaveButton
label="Save and next step"
handleSubmitWithRedirect={() => {
form.change('goNextStep', 1); // or true
props.handleSubmitWithRedirect('list');
}}
</SaveButton>
<SaveButton
label="Save only"
handleSubmitWithRedirect={() => {
form.change('validateCustom', 0); // or false
props.handleSubmitWithRedirect('list');
}}
/>
Use the validate prop on react-admin form. I could not make it work with field level validations. I had to remove every field level validation props, and implement all those in validateFunction.
Altough, you could still use the validators in your custom validation function.
const validateFunction = (values) =>{
// using our previously set custom value, which tells us which button the user clicked
let shouldValidate = values.goNextStep === 1;
// return undefined if you dont want any validation error
if (!shouldValidate) return undefined;
let errors = {};
// use built in validations something like this
var someTextFieldErrorText = required()(values.someTextField, values);
if (someTextFieldErrorText) {
errors.someTextFieldErrorText = someTextFieldErrorText;
}
// OR write plain simple validation yourself
if(!values.someTextField) {
errors.someTextField = 'Invalid property!';
}
return Object.keys(errors) ? errors : undefined;
}
Than set up tabbed form to use the previous function for validation.
<TabbedForm
validate={validateFunction}
>
...
</TabbedForm
React-admin version: 3.10.1
I have a drop down component that looks like this:
{...}
this.state = {
isVisible: false
}
}
toggleDisplay() {
this.setState({isVisible: !this.state.isVisible});
}
render() {
return (
<div>
<button onClick={this.toggleDisplay()}>click</button>
{this.state.isVisible ? <MenuElements toggleDisplay={this.toggleDisplay} /> : '' }
</div>
)
}
}
"MenuElements" is just a ul that has a li. On another page i am using this component multiple times, so whenever i click on the button, "MenuElements" is shown for each click. The problem is that i want only one component to be displayed. So if a MenuElements component is already displayed, if i click on another button, it closes the previous component, and opens the second one.
How could this be implemented in my code?
Thanks.
You will somehow need to have a single state that defines which MenuItem is displayed. You could go with a global state with something like Redux, but if you are trying to build a reusable component, I guess it'd be best to wrap all of the MenuItem components in a parent component and keep a state there. That, I think, is the React way of doing it. Read this for an idea of how to design components: https://facebook.github.io/react/docs/thinking-in-react.html.
BTW, I think there is an error in the Button onClick handler. It should be:
<button onClick={this.toggleDisplay.bind(this)}> // or bind it somewhere else
Also, the correct way to change state based on previous state is this:
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
I'd say this is du to the context of your callbacks. Have you tried forcing the context ?
<div>
<button onClick={this.toggleDisplay.bind(this)}>
click
</button>
{this.state.isVisible ?
<MenuElements toggleDisplay={this.toggleDisplay.bind(this)} />
: '' }
</div>
I'm working on a filter for a ListView, a way to be able to sort/order/etc the items. Basically I'm saving the parameters in state and they're updated via some toggles/select-fields on a <Modal>.
The modal has a cancel & apply button. If you select apply after changing filters, the ListView's contents would be updated. However if they were to select cancel after changing settings, they would be reverted to whatever it was before the filter modal was launched.
So I'm doing this:
// Update filterValues state
adjustFilterValue(filterSection, newValue) {
if ( this.state.hasAdjustedFilters === false ) {
const filterValues = this.state.filterValues;
this.setState({
hasAdjustedFilters: true
})
}
var newFilterValues = defaultFilterValues;
newFilterValues[filterSection] = newValue;
this.setState({
filterValues: newFilterValues
})
}
However whenever I adjust this.state.filterValues - newFilterValues get's updated too.
How can I save & isolate an object from state?
You can store your initial state by using lifecycle hooks:
componentDidMount() {
this._initialState = this.state;
}
Later on, in a case of revert, simply setState(this._initialState);