React Little State Machine clear data - reactjs

state-machine with react-hook-form to make my forms, but after submit the form i want to clear the storage after submit;
This is how i create my store;
createStore({
data: {}
});
And this is my Submit function
const onSubmit = (data:any) => {
action(data);
props.onSubmit(state.data);
// I need a function to clear the data to not complete my forms after i submit
}
Here a little example that i want:
https://codesandbox.io/s/delete-data-little-state-machine-q3w0g
In "step3"i want to clear the data after click on button

It looks like you need to create another action and pass that to the hook. You can see examples of this in the docs. Here is a working example:
clearAction.js
export default function clearAction(state, payload) {
return {
data: {}
};
}
Step3.js
import React from "react";
import { useForm } from "react-hook-form";
import { withRouter } from "react-router-dom";
import { useStateMachine } from "little-state-machine";
import clearAction from "./clearAction";
const Step3 = (props) => {
const { register, handleSubmit } = useForm();
const { state, action } = useStateMachine(clearAction);
const onSubit = (data) => {
action(data);
props.history.push("./resultFinal");
console.log(state, action);
action();
};
return (
<form onSubmit={handleSubmit(onSubit)}>
<h2>Clear Data</h2>
<input type="submit" />
</form>
);
};
export default withRouter(Step3);
Note in the examples provided in the docs you can pass multiple actions to the hook as needed.

Related

react hook form not getting context on nested component

I am having multiple tab, each tabs load different components. In each components I have different set of forms. So there is a generic footer where user can click on save or continue.
So I am trying to use the react hook form useContext, but I am not getting the values.
I have reproduced the same issue on the below code.
SaveAndContinue component
import React from "react";
import { useFormContext } from "react-hook-form";
const Footer = (props) => {
const { formState, handleSubmit } = useFormContext();
const onSaveDetails = (data) => {
console.log("onSaveDetails", data);
};
const onContinue = (data) => {
console.log("onContinue", data);
};
return (
<>
<button
disabled={!formState?.isDirty}
onClick={handleSubmit(onSaveDetails)}
>
Save
</button>
<button disabled={!formState?.isDirty} onClick={handleSubmit(onContinue)}>
Continue
</button>
</>
);
};
export default Footer;
How can I get the formData of each component form when clicking on the save or continue button using react hook form context
Any help is appreciated
Here is the codesandbox
I looked at your codesandbox. the problem is:
on your ServiceDetails and UserDetails components you should register your inputs using useFormContext not useForm:
const { register } = useFormContext({
mode: "onBlur",
reValidateMode: "onChange",
defaultValues: {
firstName: ""
}
});

React-Redux - How to redirect the page after storing the data in redux store?

I am trying out a react-redux sample code where I would like to add a course in one form upon clicking 'Add Course', I want to update the store and redirect to a new page with the list of courses.
But for some reason, the redirect happen after calling the redux action creator. It stays in the same page.
Any ideas how to redirect the results to a different page?
import React from "react";
import { connect } from "react-redux";
import * as courseActions from "../../redux/actions/courseActions";
import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
import history from './history'
class CoursesPage extends React.Component {
state = {
course: {
title: "",
},
};
handleSubmit = (event) => {
event.preventDefault();
this.props.actions.loadCourses.createCourse(this.state.course).then(() => {
alert('Inside Promise')
history.push('/AllCourses'); //This doesn't get executed.
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<h2>Courses</h2>
<h3>Add Course</h3>
<input type="submit" value="Add Course" />
{this.props.courses.map((course) => (
<div key={course.title}>{course.title}</div>
))}
</form>
<hr />
</div>
);
}
}
CoursesPage.propTypes = {
courses: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired,
};
function mapStateToProps(state) {
return {
courses: state.courses,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: {
loadCourses: bindActionCreators(courseActions, dispatch),
},
};
}
export default connect(mapStateToProps, mapDispatchToProps)(CoursesPage);
Action Code:
import * as types from "./actionTypes";
export function createCourse(course) {
return { type: types.CREATE_COURSE, course };
}
Reducer:
import * as types from "../actions/actionTypes";
export default function courseReducer(state = [], action) {
debugger;
switch (action.type) {
case types.CREATE_COURSE:
return [...state, { ...action.course }];
default:
return state;
}
}
history.js
import createHistory from 'history/createHashHistory'
export default createHistory()
You can create a custom middleware to do so:
const hasAddedData = (state) => {
// Your checks
}
const redirectMiddleware = history => storeAPI => next => action => {
console.log('dispatching', action)
const result = next(action);
// If the action is the one you want to trigger the redirect (action.type)
// and the state pass the checks (storeAPI.getState()),
// do the redirect.
//
// This would be like:
if (action.type === types.CREATE_COURSE && hasAddedData(storeAPI.getState())) {
history.push(destinationPath);
}
// You must return the result of next(action) to avoid breaking Redux.
return result;
}
And wherever you create your Redux Store:
// history should already be provided by React Router.
const middlewareEnhancer = applyMiddleware(redirectMiddleware(history))
const store = createStore(yourRootReducer, middlewareEnhancer)
If you need to check the previous state too, just set a const with storeAPI.getState() before running next(action). You can expand your middleware with other redirect checks or scenarios you need for additional actions.
WARNING: I wanted to give you the vanilla code solution, but keep in mind these three things:
This is a task that is probably better and opinionatedly made by a library (check connected-react-router).
Also, instead of making a custom middleware for action specific tasks, you can use a widely accepted middleware library such as redux-saga.
Think about your app workflow. Do you need additional state properties (done flags, selection properties...)? Are all the CREATE_COURSE actions going to redirect or only a fraction of them? Will a specific REDIRECT action make things easier for you? Do you really need an imperative redirect or would it be possible, with the right state structure, a declararive Redirect with React Router component?

On click returns null instead of an object

It's really basic I guess. I'm trying to add onClick callback to my script & I believe I'm missing a value that would be responsible for finding the actual item.
Main script
import React from 'react';
import { CSVLink } from 'react-csv';
import { data } from 'constants/data';
import GetAppIcon from '#material-ui/icons/GetApp';
import PropTypes from 'prop-types';
const handleClick = (callback) => {
callback(callback);
};
const DownloadData = (props) => {
const { callback } = props;
return (
<>
<CSVLink
data={data}
onClick={() => handleClick(callback)}
>
<GetAppIcon />
</CSVLink>
</>
);
};
DownloadData.propTypes = {
callback: PropTypes.func.isRequired,
};
export default DownloadData;
Storybook code
import React from 'react';
import DownloadData from 'common/components/DownloadData';
import { data } from 'constants/data';
import { action } from '#storybook/addon-actions';
export default {
title: 'DownloadData',
component: DownloadData,
};
export const download = () => (
<DownloadData
data={data}
callback={action('icon-clicked')}
/>
);
So right now with this code on click in the storybook I'd get null and I'm looking for an object.
One of the potential issues I can see is that your handleClick function is stored as it is in-memory, when you import the component. That means you're keeping reference of something that doesn't exists and expects to use it when rendering the component with the callback prop.
Each instance of a component should have its own function. To fix it, move the function declaration inside the component. Like this:
const Foo = ({ callback }) => {
// handleClick needs to be inside here
const handleClick = callback => {
console.log("clicked");
callback(callback);
};
return <div onClick={() => handleClick(callback)}>Click me!</div>;
};
Check this example.
If this doesn't fix your problem, then there is something wrong with how you're implementing Storybook. Like a missing context.

Creating a custom action in react-admin I cannot see where to link to my dataprovider

I am following this tutorial to create custom actions in React-admin (former Admin-on-rest): using a custom action creator.
However, after implement it, my code is not doing anything, i.e., the backend is not called.
I guess it is missing in the documentation a way to link the action with the dataProvider, unless the redux is handling it automagically.
Is it right? No need to link to the dataProvider being the opposite from what was made in the fetch example?
Following pieces of my code:
UpdatePage is pretty similar to ApproveButton in the tutorial:
// Update Page.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { updatePage } from '../../actions/pages';
import Button from '#material-ui/core/Button';
class UpdatePage extends Component {
handleClick = () => {
const { record } = this.props;
updatePage(record.id, record);
}
render() {
return <Button disabled={this.props.disabled} onClick={this.handleClick}>Confirmar</Button>;
}
}
UpdatePage.propTypes = {
disabled: PropTypes.bool.isRequired,
updatePage: PropTypes.func.isRequired,
record: PropTypes.object,
};
export default connect(null, {
updatePage
})(UpdatePage);`
Actions for the UpdatePage (like in the commentActions of the tutorial):
//in ../../actions/pages.js
import { UPDATE } from 'react-admin';
export const UPDATE_PAGE = 'UPDATE_PAGE';
export const updatePage = (id, data) => ({
type: UPDATE_PAGE,
payload: { id, data: { ...data, is_updated: true } },
meta: { fetch: UPDATE, resource: 'pages' },
});
You're invoking the action creator directly in the handleClick method. You need to use the updatePage function inside the props to properly dispatch the redux action.
handleClick = () => {
const { record } = this.props;
this.props.updatePage(record.id, record);
}

Uncaught TypeError: this.props.dispatch is not a function

I am trying to dispatch an action when I submit a form but I'm getting this:
Uncaught TypeError: this.props.dispatch is not a function
This is my class:
/**
*
* CatalogPage
*
*/
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { compose } from 'redux';
import { Form, Control } from 'react-redux-form/immutable';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import makeSelectCatalogPage from './selectors';
import reducer from './reducer';
import saga from './saga';
export class CatalogPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
handleSubmit = (user) => {
this.props.dispatch({ type: 'test action' });
}
render() {
return (
<Form
model="user"
onSubmit={(user) => this.handleSubmit(user)}
>
<label htmlFor=".firstName">First name:</label>
<Control.text model=".firstName" id=".firstName"/>
<label htmlFor=".lastName">Last name:</label>
<Control.text model=".lastName" id=".lastName"/>
<button type="submit">
Finish registration!
</button>
</Form>
);
}
}
CatalogPage.propTypes = {};
const mapStateToProps = createStructuredSelector({
catalogpage: makeSelectCatalogPage(),
});
function mapDispatchToProps(dispatch) {
return {
dispatch,
};
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({ key: 'catalogPage', reducer });
const withSaga = injectSaga({ key: 'catalogPage', saga });
export default compose(
withReducer,
withSaga,
withConnect,
)(CatalogPage);
I thought that the compose function at the bottom would connect my component to the store and thus have access to the dispatch function through this.props.dispatch. But it's not working, what am I missing?
Thanks!
EDIT: I've changed handleSubmit to arrow function but problem still persists
handleSubmit = (user) => {
this.props.dispatch({ type: 'test action' });
}
EDIT: The problem resolved itself
Something to mention is that react-boiler-plate is not as user-friendly as one might expect. There's alot of weird things that are happening and took me a long time to debug.
The problem here is the misunderstanding of a class method and the way that React manages instances.
You can do three things to avoid this problem:
1) Convert the (handleSubmit) function to an arrow function so in that case, it won't have its own this.
handleSubmit = (user) => { // ...logic here }
2) Create a constructor inside the component and do the next step:
this.handleSubmit = this.handleSubmit.bind(this)
In this case, you attach this to the function each time an instance is created.
3) When you call the method inside the render use the .bind() to bind this:
onSubmit={(user) => this.handleSubmit.bind(this, user)}
You don't need mapDispatchToProps if you just want to inject dispatch into your component. You would use that when you want to bind your action creators before injecting them into your component. Just pass mapStateToProps with no second param and you should be good.
You also need to do what Jose Gomez suggests below. Basically you need to bind this. The easiest way is to just change handleSubmit to an arrow function
handleSubmit = user => {
...
}

Resources