React this.setState not working with context API - reactjs

Currently I have this code from my AppContext.js file
import React, { Component, createContext } from 'react';
export const AppContext = createContext();
export class AppProvider extends Component {
state = {
test: '',
};
getEntries() {
console.log('FIRED');
this.setState({test: 'HELLO'});
}
render() {
return (
<AppContext.Provider
value={{
...this.state,
getEntries: this.getEntries
}}
>
{this.props.children}
</AppContext.Provider>
);
}
}
I'm calling the getEntries function and its displaying message from the console successfully but the this.setState is not working it says TypeError: this.setState is not a function

The problem here is this is not bound to the right context.
The simplest workaround is probably to use this syntax:
getEntries = () => {
...
}
There are several ways in React to bind this to the class context: check this article for other ways.

getEntries function needs to be bind to the component. The simple way to do it is to use arrow function as shown below.
getEntries = () => {
console.log('FIRED');
this.setState({test: 'HELLO'});
}
The second way to bind getEnteries method to the component is
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.getEntries = this.getEntries.bind(this);
}

Related

Is there any way to export a function from a class component?

class Options extends Component {
const indexHandler = (index) => {
const { userAnswer, answer, score } = this.state;
this.setState({
currentIndex: index,
});
};
render(){
return(){
}
}
}
export {
indexHandler,
};
here I am unable to export this function!
but if the same function is declared outside the class component, the function can be exported!
Any help regarding this would be great!
There are two thing i need to explain.
you’re trying to pass state, functions in the wrong way.
classes won't work differently in React, JS also. a class is just a blueprint for an object. The class create an instance of an object:
export {
indexHandler,
};
export is done outside the class. So this won't pass the function you referenced in the Option Class.
You can't export things inside functions or classes in javascript.
to get the access for indexHandler method you need to pass indexHandler as a prop from Option component to use indexHandler for its descendants.
For example
class Options extends Component {
indexHandler = (index) => {
const { userAnswer, answer, score } = this.state; // Search Component will pass the state of it via 'changeIndex' and can change the state we need here.
this.setState({
currentIndex: index,
});
};
render() {
return <Search changeIndex={this.indexHandler.bind(this)} />;
}
}

Invoking functions on nested components in react redux

Sorry if this question displays a lack of understanding of the react paradigm which I suspect it does however I am very new to the tech.
I want to dispatch an action from my 'actions' code which should ultimately call a function on a nested component. Up to now I have been simply modifying state by dispatching actions and catching them in the reducer which works fine.
I am working on a portlet as part of a wider framework and I can capture an onExport() message in the actions. From here I have no idea of the best way to call the nested component (I need access to the inner ag-grid in the nested component to export it).
I have considered introducing some new 'exportRequested' state flag and setting this in the reducer then using componentDidReceiveProps in the nested component. I have also been studying the 'connect' idea and this seems right in so far as it would allow me to expose the function and connect it to the store but I can't seem to join the dots and figure out how to invoke it from the reducer. Is there some way to sort of dispatch an action and catch it directly in the nested component?
some code:
Container:
import {initData} from '/actions';
export class MainComponent extends PureComponent {
static propTypes = {
initData: func.isRequired,
data: array.isRequired,
};
static defaultProps = {
data: [],
};
componentDidMount() {
this.props.initData();
}
render() {
const { data } = this.props;
return (
<div>
<ChildGrid data={data} />
</div>
);
}
}
export default connect(
state => ({
data: getData(state),
}),
{ initData }
)(MainComponent);
Nested Grid:
export class ChildGrid extends PureComponent {
static propTypes = {
data: array.isRequired,
};
static defaultProps = {
data: [],
};
exportData() {
// HOW TO MESSAGE THIS FROM ACTIONS. I want to call DataGrid.gridApi.exportAsCsv()
}
render() {
const { data } = this.props;
return (
<div>
<DataGrid
rowData={data}
/>
</div>
);
}
}
You thought of the correct solution to your problem, by creating a state flag in your redux store then listening to the change of that property in your nested component. Unfortunately in Redux we can't listen to specific events or specific state property changes of the Redux store.
The implementation of such a solution is as follows:
ChildGrid.jsx
class ChildGrid extends PureComponent {
static propTypes = {
data: array.isRequired,
};
static defaultProps = {
data: [],
};
componentWillReceiveProps(newProps) {
if (newProps.exportRequested) {
this.exportData();
}
}
exportData() {
DataGrid.gridApi.exportAsCsv();
}
render() {
const { data } = this.props;
return (
<div>
<DataGrid
rowData={data}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
reduxExportRequested: state.exportRequested
};
};
export default connect(mapStateToProps)(ChildGrid);

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);

dispatch is not defined on my functions using react and redux

I am trying to use react-redux-loading-bar to show a loading bar during fetching data from API servers, I don't use promise middleware so I decided to use it without, the example says do this
import { showLoading, hideLoading } from 'react-redux-loading-bar'
dispatch(showLoading())
// do long running stuff
dispatch(hideLoading())
And it gives me this.
Uncaught ReferenceError: dispatch is not defined
I had similar issues with other libraries and gave up that time, this time I want to actually understand how this works, so any info is greatly appreciated. Heres the code that causing the error, speicifc function and class names stripped.
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { showLoading, hideLoading } from 'react-redux-loading-bar'
import * as xxxxxActions from '../../actions/xxxxx'
class xxxxxx extends React.Component {
constructor(props) {
super(props)
this.handleclick = this.handleclick.bind(this)
}
handleclick(){
dispatch(showLoading())
asynchronousGetFunction( target_url, function (data) {
dispatch(hideLoading())
})
}
render() {
return <li onClick={this.handleclick}>yyyyyyy</li>
}
}
function mapStateToProps( state ){
return {
}
}
function mapDispatchToProps(dispatch, state) {
return {
xxxxxActions: bindActionCreators( xxxxxActions, dispatch )
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(xxxxxx)
Once you connect your component, dispatch becomes a prop. The same applies for xxxxxActions...
In that case, the handle would be:
handleclick(){
this.props.dispatch(...)
}
You need to pass dispatch function to your props:
function mapDispatchToProps(dispatch, state) {
return {
xxxxxActions: ....,
showLoading: function () {
dispatch(showLoading());
},
hideLoading: function () {
dispatch(hideLoading());
},
};
}
Then, use it in your component:
this.props.showLoading();
...
this.props.hideLoading();
You don't need use "dispatch" in components. Bind your functions with dispatch in mapDispatchToProps.
Read more about mapDispatchToProps.

react redux props undefined on event listener when using mapDispatchToProps

My first time using react/redux and I'm trying to bind a simple action addClick to the 'click' event, but when I click I receive the error:
Uncaught TypeError: Cannot read property 'props' of undefined
My (stripped down) code is:
import {addClick} from './actions'
const mapDispatchToProps = {addClick}
class App extends Component {
componentDidMount() {
document.addEventListener('click', this.props.addClick)
}
componentWillUnmount() {
document.removeEventListener('click', this.props.addClick)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
Before I had it implemented without mapDispatchToProps using an action creator defined within the class and bound to this in the constructor. But I thought part of the point of mapDispatchToProps was to bind the action creator to this (as well as wrapping it in a dispatch)
What am I missing?
Thanks!
From what I can tell from the documentation, the object short-hand (const mapDispatchToProps =
{addClick}) you use for mapDispatchToProps doesn't bind this to anything. It just sees to it that your addClick action creator gets called with dispatch. So that if you, in your component execute addClick(3), then that will result in a call looking like this dispatch(addClick(3)).
I'm not sure why your action creator would need access to this though. Can't you just pass it what ever data it needs as a parameter? So that the call in your component might look like
componentDidMount() {
const {addClick, someOtherProp} = this.props;
document.addEventListener('click', () => addClick(someOtherProp));
}
Do you use props inside addClick action?
Check this example:
import React from "react";
import { render } from "react-dom";
import { connect, Provider } from "react-redux";
import { createStore } from "redux";
function addClick(event) {
return {
type: "CLICK",
payload: `pageX: ${event.pageX} | pageY: ${event.pageY}`
};
}
const mapStateToProps = state => {
return {
clickXY: state
};
};
const mapDispatchToProps = { addClick };
class App extends React.Component {
componentDidMount() {
document.addEventListener("click", this.props.addClick);
}
componentWillUnmount() {
document.removeEventListener("click", this.props.addClick);
}
render() {
return (
<h1>
Click message: {this.props.clickXY}
</h1>
);
}
}
function clickReducer(state = "None", action) {
switch (action.type) {
case "CLICK": {
return action.payload;
}
default:
return state;
}
}
let store = createStore(clickReducer);
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
class Root extends React.Component {
render() {
return (
<Provider store={store}>
<AppContainer />
</Provider>
);
}
}
render(<Root />, document.getElementById("root"));
Link to editor
So bind works well in this code.

Resources