Accessing this.context from onSubmitSuccess of redux-form - reactjs

Here is the problem I am facing:
I am using react-router and redux-form in my application. In one of my forms, I want to use the router to go back to the main page after the submission succeeds. Here is the code I have:
function Router(target) {
target.contextTypes = target.contextTypes || {};
target.contextTypes.router = React.PropTypes.func.isRequired;
}
enter code here
#Router
#reduxForm({
form: 'editLocation',
fields: LocationFormFields,
validate: ValidateLocationForm,
onSubmitSuccess: () => {
var path = '/locations';
this.context.router.push(path);
},
onSubmitFail: () => {
console.log('failed');
}
}, MapStateToProps)
export class EditLocation extends React.Component .....
The problem is that 'this.context' is not defined inside the onSubmitSucess method.
I know that I can take care of this using browserHistory directly. However, I don't want to really go that route and I want to use this.cotext. Any ideas?

I think your best bet is to not make your form component the same as your route component, and pass in the onSubmitSuccess as a prop to your form component.
import { withRouter } from 'react-router'
#withRouter // injects router as prop
export class EditLocation extends React.Component {
handleSubmitSuccess() {
this.props.router.push('/locations')
}
render() {
return <EditLocationForm
onSubmitSuccess={this.handleSubmitSuccess}/>
}
}
#reduxForm({
form: 'editLocation',
fields: LocationFormFields,
validate: ValidateLocationForm,
onSubmitFail: () => {
console.log('failed');
}
}, MapStateToProps)
export class EditLocationForm extends React.Component .....

To use context inside a component you need to specify contextTypes
static contextTypes = {
router: PropTypes.object
};
So I would recommend either to call you function from inside the component and specify contextTypes there or add one higher order component to handle that.

Related

How can I obtain class props from url and store in react-redux

so I am trying to pass params using route to a react component and also at the same time use Component class props. Here is what am doing
import { loadSchemes, } from '../../actions/schemes;
export class Schemes extends Component {
constructor(props) {
super(props);
const { match: { params } } = this.props;
this.state = {
client_id: params.pk,
}
}
componentDidMount() {
this.props.loadSchemes();
}
render(){
return(
<div>
{this.props.schemes_list.map((scheme,index)=><p key={index}>{scheme}</p>)}
</div>
)
}
}
const mapStateToProps = (state) => ({
schemes_list: state.schemes,
});
export default connect(mapStateToProps,{ loadSchemes,})(Schemes);
And I have a url to this component as
<Route path="/client/:pk/schemes" component={Schemes}/>
The problem is I get an error this.props.schemes_list is undefined and this.props.loadSchemes is undefined
please help am using react-redux
Obviousely in component from where you call Scheme, you import { Schemes }, an unconnected component, instead of Schemes - default connected component. Please check it.

Best way to sharing function in react

I'm new in react. I wonder what is the best option for sharing functions (except inheritance). In inheritance way I can do something like this:
class BaseForm {
updateForm() => {};
}
class MyFormOne extends BaseForm {
// this.updateForm();
}
And I can code few Form components which extends BaseForm and I can use updateForm method in every. What is the best way in react to do this?
There are multiple ways for the question :
1) If the shared fnc is the static function (independent with the component). You should create a common file utils.js and then export, import function wherever you want
// utils.js
export function updateForm() { }
// MyformOne.js
import {updateForm} from './utils';
// Use fnc updateForm() in MyFormOne component
2) If the shared fnc is dependent with class component, you should use the design pattern in React : HOC (Higher Order Component) or render Props for sharing code (states, functions) between React components (render your code more abstract => a good practice for reusable)
class BaseForm extends React.Component {
constructor(props) {
super(props);
this.state = {
a1 : "",
a2 : "",
}
}
// example : share the func updateForm of Baseform
updateForm = () => {}
render() {
return (
<div>
{/*
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state, this.updateForm)}
</div>
);
}
}
class WithBaseForm extends React.Component {
render() {
return (
<div>
<BaseForm render={(stateBaseForm, updateForm) => (
<MyFormOne stateBaseForm={stateBaseForm} updateForm={updateForm}/> // so MyFormOne could use the fnc updateForm of BaseForm
)}/>
</div>
);
}
}
You should view the link for more details :
https://en.reactjs.org/docs/render-props.html
https://en.reactjs.org/docs/higher-order-components.html
You can create a utils.js file and place your common JS functions there. Something like this:
export const updateFormCommon = (inputValue) => {
// Your function logic here
return something;
};
export const showMessage = () => {
alert('hello world');
};
Now you can import utils.js wherever you need and call your common\shared functions like this:
import {updateFormCommon, showMessage} from './utils'
let result = updateFormCommon(1000)}
showMessage();

How to get the data from React Context Consumer outside the render

I am using the new React Context API and I need to get the Consumer data from the Context.Consumer variable and not using it inside the render method. Is there anyway that I can achieve this?
For examplify what I want:
console.log(Context.Consumer.value);
What I tested so far: the above example, tested Context.Consumer currentValue and other variables that Context Consumer has, tried to execute Context.Consumer() as a function and none worked.
Any ideas?
Update
As of React v16.6.0, you can use the context API like:
class App extends React.Component {
componentDidMount() {
console.log(this.context);
}
render() {
// render part here
// use context with this.context
}
}
App.contextType = CustomContext
However, the component can only access a single context. In order to use multiple context values, use the render prop pattern. More about Class.contextType.
If you are using the experimental public class fields syntax, you can use a static class field to initialize your contextType:
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
/* render something based on the value */
}
}
Render Prop Pattern
When what I understand from the question, to use context inside your component but outside of the render, create a HOC to wrap the component:
const WithContext = (Component) => {
return (props) => (
<CustomContext.Consumer>
{value => <Component {...props} value={value} />}
</CustomContext.Consumer>
)
}
and then use it:
class App extends React.Component {
componentDidMount() {
console.log(this.props.value);
}
render() {
// render part here
}
}
export default WithContext(App);
You can achieve this in functional components by with useContext Hook.
You just need to import the Context from the file you initialised it in. In this case, DBContext.
const contextValue = useContext(DBContext);
You can via an unsupported getter:
YourContext._currentValue
Note that it only works during render, not in an async function or other lifecycle events.
This is how it can be achieved.
class BasElement extends React.Component {
componentDidMount() {
console.log(this.props.context);
}
render() {
return null;
}
}
const Element = () => (
<Context.Consumer>
{context =>
<BaseMapElement context={context} />
}
</Context.Consumer>
)
For the #wertzguy solution to work, you need to be sure that your store is defined like this:
// store.js
import React from 'react';
let user = {};
const UserContext = React.createContext({
user,
setUser: () => null
});
export { UserContext };
Then you can do
import { UserContext } from 'store';
console.log(UserContext._currentValue.user);

React - Getting refs to wrapped class components

I have a map component that contains a child sidebar component. I am trying to do a relatively simple task of scrolling to the place in the list of places in the sidebar when it's map marker is clicked on. But, because the sidebar needs to be wrapped in withRouter and connect, I'm unable to set a ref (ref) => this.sidebar = ref in the map component.
export class Map extends React.Component {
...
handleClick() {
this.sidebar.scrollToPlace(place.id);
}
render () {
return (
<MapSidebar
// unable to set ref
/>
)
}
}
and
class MapSidebar extends React.Component {
...
scrollToPlace(id) {
this.refs[id].scrollIntoView({block: 'end', behavior: 'smooth'});
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MapSidebar));
I know that using wrappedComponentRef could get me the contents of withRouter, but then I still have connect to deal with.
I also tried creating a custom ref on the MapSidebar instance:
<MapSidebar
getReference={(ref) => {
this.sidebar = ref;
}} />
and then in the MapSidebar class constructor, calling:
if(this.props.getReference) {
this.props.getReference(this);
}
but that resulted in an infinite loop of that component updating (although I'm not sure I understand why).
Is there a better way to get around these issues?
I suggest you avoid refs and simply pass the scroll value down:
export class Map extends React.Component {
...
handleClick() {
this.setState({scrollToPlaceId: place.id});
}
render () {
return (
<MapSidebar
// Add a new property
scrollToPlace={this.state.scrollToPlaceId}
/>
)
}
}
Then in your sidebar component, just listen to scroll changes in componentWillReceiveProps for example
class MapSidebar extends React.Component {
...
componentWillReceiveProps(nextProps) {
if (nextProps.scrollToPlace !== this.props.scrollToPlace) {
this.refs[nextProps.scrollToPlace].scrollIntoView({block: 'end', behavior: 'smooth'});
}
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MapSidebar));
Store a reference in both classes:
// MapSidebar render - add this to the element you want.
<div ref={r => (this.ref = r)}>
Then in Map render:
<MapSidebar ref={r => (this.sidebar = r)}>
Now after Map has mounted you have access to the ref:
this.sidebar.ref

Missing context while using #connect

I have React app which uses react-redux library. It works fine, I get my store context, I can dispatch actions. Beautiful.
Now I have met a problem. I want to declare child context in a root component and use it to pass global function right into the children.
export default class Root extends React.Component {
globalFunc() {
}
getChildContext() {
return {
globalFunc: this.globalFunc
};
}
render() {
return (
{ /* ChildComponent somewhere in here */ }
);
}
}
Root.childContextTypes = {
globalFunc: PropTypes.func
}
The problem is inside one of the children I get empty object when I have #connect decorator from react-redux. When I remove the decorator, I get my context correctly. Why Redux removes the context? How to make a workaround?
#connect(mapStateToProps)
export default class ChildComponent extends React.Component {
constructor(props, context) {
super(props, context);
console.log(this.context); // EMPTY {}
}
render() {
// ...
}
}
ChildComponent.contextTypes = {
store: PropTypes.store.isRequired,
globalFunc: PropTypes.func
}
I had a similar issue and when I used the "normal" HoC form for the connect() function instead of the decorator one, it was fixing the issue.
Note that this is not recommended to use the decorator form as said there by the creator of Redux: https://stackoverflow.com/a/35642430/757461
Also if you really want to use decorators, I could fix this issue by defining my context using a decorator. Something similar to this:
export function context(contextTypes, context) {
return DecoratedComponent => {
return class extends Component {
static childContextTypes = contextTypes;
getChildContext() {
return context(this.props)
}
render() {
return <DecoratedComponent {...this.props} />
}
}
}
}
And then you use it like this:
#connect(mapStateToProps, mapDispatchToProps)
#context(contextTypes, getChildContext)

Resources