How can I access another function inside the react Context.Provider value? - javascript

At the moment, I learn how to use react context API.
I have a react Provider class, with some state data and functions in the value={}. How can I access a function inside this value from another function inside this value?
So, the function1() is called by a child component. When the state change is finished, I want to call the function2() and access the new state.
Is it possible in react to get something like this?:
class Provider extends Component {
state = {
foo: 'bar'
}
render() {
return() {
<Context.Provider value={{
state: this.state,
function1: () => {
this.setState({ foo: 'changed' }, () => {
// HERE I WANT TO CALL THE NEXT FUNCTION IN THIS VALUE
this.function2()
});
},
function2: () => {
// called by function1 after state change
console.log(this.state)
}
}}>
{ this.props.children }
</Context.Provider>
}
}
}
If I try to run this, and fire the function1() from the child, it gives me
TypeError: _this2.function2() is not a function
I don't understand, why it is trying to access a _this2 variable, because I defined it to access this.function2().
Is it just not possible to do what I want in react? You might say, why the function2() is an extra function, and why I don't add the code in the end of the function1(). It's because the function2() has to be a independent accessable function.
Can anyone help me? Thanks!

What you can try to do is this:-
class Provider extends Component {
state = {
foo: 'bar'
}
an_independent_function() {
console.log(this.state);
}
render() {
return() {
<Context.Provider value={{
state: this.state,
function1: () => {
this.setState({ foo: 'changed' }, () => {
// HERE I WANT TO CALL THE NEXT FUNCTION IN THIS VALUE
this.an_independent_function();
});
},
function2: this.an_independent_function.bind(this)
}}>
{ this.props.children }
</Context.Provider>
}
}
}

Related

How do you render a component based on an AsyncStorage value in React Native?

I have a component that looks like this:
export default class Class1 extends Component {
render = async () => {
await AsyncStorage.getItem('someValue', (error, someValue) => {
return(
<Class2 prop1={someValue}/>
)
}
}
}
What's happening here is that I need to render Class1 based on the value of someValue that is returned from AsyncStorage. The problem is, you can't make the render() method async because async functions return a promise, and render() needs to return a React component.
Does anyone know how I can do this?
Thanks!
For this kind of tasks, you would put the value in your state. And based on the state, you will render the class as required.
In your componentDidMount of Class1, write:
componentDidMount() {
AsyncStorage.getItem('value').then((val) => {
this.setState({ value: val });
})
}
Then in your Class1 add a method which will generate the class based on state value:
createClass() {
// do something with the value
if (this.state.value === somevalue) {
return (
<Class2 />
)
}
return null;
}
And in your render method of Class1, type:
render() {
return (
{ this.createClass() }
)
}
You can set it to state, for example:
componentDidMount() {
AsyncStorage.getItem('someValue', (e, someValue) => {
this.setState({someValue})
}
}
Then you can use someValue from state in your render.
Currently, in addition to the async render issue, since you're already passing in a callback to AsyncStorage.getItem(), I'm not sure what using async/await would do.

Access vue method within vuex mapState

I have an important task to do while data is computed within a vuex mapState. I need to call this vue method countAlerts every time data is changed; to do that the computed property needs to call that method but this scope has no vue methods when it is used insight vuex mapState.
export default {
name: "Alerts",
methods: {
countAlerts(data, period) {
/// DO SOMETHING, THEN RETURN DATA
return data;
}
},
computed: {
...mapState({
foundation: state => state.insights.foundation,
insights: state => {
return state.insights.list.filter(al => {
switch (state.insights.foundation.period) {
case "daily":
// ====>> NEED TO CALL METHOD HERE <<=====
al = this.countAlerts(al, "daily");
if (
al.threeDayUp ||
al.threeDayDown ||
al.greatDayUp ||
al.greatDayDown
) {
return al;
}
break;
/// MORE CODE ABOVE
}
});
}
})
}
};
this is bound to the component's contex when you define computed props as functions.
From the docs:
// to access local state with `this`, a normal function must be used
countPlusLocalState (state) {
return state.count + this.localCount
}

React Classes: Referencing class as "this", within an object's function property

I have finally gotten into using react and ES6 and it's going well but I am finally stumped and could use some direction.
I have got my head around binding this to a method to reference the class, but I am trying to go a bit deeper. Take this for example...which works as expected:
class App extends Component {
state = {
myFirstState: false,
};
handleMyFirstState = () => {
this.setState( { myFirstState : true } );
};
render() {
return (
<MyComponent handleMySate={ this.handleMyState } />
);
}
}
export default App;
As the amount of methods increased I decided NOT to pass each method individually as props and to group them in an object first, and to just pass the object as a whole, as a prop. Like So...
class App extends Component {
state = {
myFirstState: false,
mySecondState: false
};
handleMyFirstState = () => {
this.setState( { myFirstState : true } );
};
handleMySecondSate = () => {
this.setState( { mySecondState : true } );
};
render() {
const handleStates = {
first : this.handleMyFirstState,
second : this.handleMySecondState
}
return (
<MyComponent handleStates={ handleStates } />
);
}
}
export default App;
Now, I am trying to avoid redundant code and just build the methods as one object with functions as properties before the render begins. Pretty much like this...
class App extends Component {
state = {
myFirstState: false,
mySecondState: false
};
handleStates = {
// Here is where 'this' does not reference the App class
// I get results from the console log but setstate doesn't pass correctly
first : () => { console.log("First Triggered"); this.setState( { myFirstState : true } ); },
second : () => { console.log("Second Triggered"); this.setState( { mySecondState : true } ); }
};
render() {
return (
<MyComponent handleStates={this.handleStates} />
);
}
}
export default App;
// I trigger the function like this within MyComponent and I get the console log, but `this.setState` breaks.
<Button onClick={ this.props.handleState.first } >Handle First</button>
I have successfully triggered the functions from the child component ,<MyComponent/>, using the latter code, but this no longer refers to the class and I can't figure out how to bind this to handleStates since it's not a function.
Is this just not possible or is there another way to handle what I am trying to achieve?
Thank you in advance!
ADDITIONAL
If I move the handleStates into the render() it works just fine...how could that be?
class App extends Component {
state = {
myFirstState: false,
mySecondState: false
};
render() {
const handleStates = {
first : () => { this.setState( { myFirstState : true } ); },
second : () => { this.setState( { mySecondState : true } ); }
};
return (
<MyComponent handleStates={this.handleStates} />
);
}
}
export default App;
First, in the second example, you pass this.handleStates as the value for the prop handleStates, but it's undefined. You built handleStates as a local variable, and thus you want your props to reference that local variable:
<MyComponent handleStates={handleStates} />
For your third (last) example, your issue is even simpler: you defined handleStates as an attribute on this which is assigned an object, itself with two attributes, first and second, each of which have a function as their value.
When you ultimately pass this.handleStates to MyComponent, you're passing an object, not a function. If you want to call one of first or second from MyComponent, you can do so like this:
this.props.handleStates.first()
Which has the desired result of updating the state in App.
For what it's worth, there's a more common pattern for this: simply pass a single updater function as the prop, named according to what it does:
class Sandwich extends React.Component {
this.state = {
bread: "",
meat: "",
veggie: "",
}
updateSandwich = (component, selection) => {
this.setState({ [component]: selection })
}
render() {
return(<IngredientSelector updateSandwich={this.updateSandwich} />)
}
}
class IngredientSelector extends React.Component {
return(){
<button value="Rye" onClick={() => this.updateSandwich("bread", "rye")} />
<button value="Wheat" onClick={() => this.updateSandwich("bread", "wheat")} />
<button value="Ham" onClick={() => this.updateSandwich("meat", "ham")} />
<button value="Turkey" onClick={() => this.updateSandwich("meat", "turkey")} />
}
}

React child state update based on parent's prop change

I am trying to implement minesweeper with React. I have a Header component (not shown) where the dimensions and the number of bombs can be changed so the App component state (sizeX, sizeY and bombNumber) will be updated, and these values will be sent down to the Table component as props.
My problem is that the Table component state is dependent on App component's props and when the createTable()method gets invoked on prop updates it uses the updated sizeX and sizeY props (which is good of course) but for some reason it uses the previous value of the bombNumber prop.
Is this the right approach? If so what did I miss?
class App extends Component {
constructor (props) {
super(props);
this.state = {
sizeX: DEFAULT_SIZE,
sizeY: DEFAULT_SIZE,
maxBombNumber: DEFAULT_BOMB_NUMBER,
bombNumber: DEFAULT_BOMB_NUMBER
}
updateSize (val, side) {
this.setState({[side]: val}, function () {
this.setState({maxBombNumber: this.calculateMaxBombNumber()}, function () {
if (this.state.maxBombNumber < this.state.bombNumber) {
this.setState({bombNumber: this.state.maxBombNumber}, function (){
});
}
});
});
}
render() {
return (
<div className="App">
<Table
sizeX={this.state.sizeX}
sizeY={this.state.sizeY}
bombNumber={this.state.bombNumber}
/>
</div>
);
}
}
Table.js
class Table extends Component {
constructor (props) {
super(props);
this.state = {
table: this.createTable()
}
componentWillReceiveProps(nextProps) {
this.setState({table: this.createTable()});
}
createTable() {
// function using sizeX, sizeY and bombNumber
}
}
Update:
The console log here prints the right values if I change the state, but the child will use the old value for bombNumber.
updateSize (val, side) {
this.setState({[side]: val}, function () {
this.setState({maxBombNumber: this.calculateMaxBombNumber()}, function () {
if (this.state.maxBombNumber < this.state.bombNumber) {
this.setState({bombNumber: this.state.maxBombNumber}, function (){
console.log(this.state);
});
}
});
});
}
Try to wrap all setState calls in one:
updateSize(val, side) {
const maxBombNumber = this.calculateMaxBombNumber();
const bombNumber = maxBombNumber < this.state.bombNumber ? maxBombNumber : this.state.bombNumber;
this.setState({
[side]: val,
maxBombNumber,
bombNumber
});
}
UPD:
componentWillReceiveProps(nextProps) {
this.setState({table: this.createTable()});
}
When Table component get new props from parent App –> componentWillReceiveProps execute and this.createTable() method use previous values not that in nextProps. In this way you should give it properly by arguments to createTable for example. Check this example.
UPD 2:
It will be better to make Table component – dump (stateless) and use state only in parent component App. Just move createTable method to App and it will be enough.
Or you can call createTable inside render method of Table.
I believe your issue is that setState is an asynchronous call, so the this.state that you're using is not updated. See this article for more information about using setState with a function. https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b
I think this would get you what you want:
updateSize(val, side) {
this.setState({ [side]: val }, function () {
this.setState({ maxBombNumber: this.calculateMaxBombNumber() }, this.setState(function (state) {
if (state.maxBombNumber < state.bombNumber) {
this.setState({ bombNumber: state.maxBombNumber }, function () {
console.log(state);
});
}
}));
});
}

React render method that depends on asynchronous request and state change

I am trying to learn ReactJS and Redux, and have come across a problem that I cannot seem to get over.
I have a React component, that gets data from an asynchronous request.
export class MyPage extends React.Component {
constructor(props) {
super(props)
this.state = {
enableFeature: false,
}
this.handleEnableFeatureChange = this.handleEnableFeatureChange.bind(this)
}
componentWillMount () {
this.fetchData()
}
fetchData () {
let token = this.props.token
this.props.actions.fetchData(token)
}
handleEnableFeatureChange (event) {
this.setState({ enableFeature: event.target.checked })
}
render () {
if (this.props.isFetching) {
return (
<div>Loading...</div>
)
} else {
return (
<div>
<label>Enable Feature
<input type="checkbox"
className="form-control"
checked={this.props.enableFeature}
onChange={this.handleEnableFeatureChange}
/>
</label>
</div>
)
}
}
}
So, my problem now is that, when I change the state of the checkbox, I want to update the state of my data. However, every time I update the state of my data, the react component method shouldComponentUpdate kicks in, and uses the current props to render the original data.
I would like to see how such cases are handled in general.
Thanks.
Try to do it like the following, i.e.
Use componentWillReceiveProps to assign props.enableFeature to state.enableFeature. From documentation
componentWillReceiveProps() is invoked before a mounted component receives new props. If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
componentWillReceiveProps() is not invoked if you just call this.setState()
Use this state to load the value of checkbox
Manipulate this state (onchange) to update the value of checkbox
Following code can work in your case
export class MyPage extends React.Component {
static propTypes = {
isFetching: React.PropTypes.bool,
enableFeature: React.PropTypes.bool,
token: React.PropTypes.string,
actions: React.PropTypes.shape({
fetchData: React.PropTypes.func
})
};
state = {
enableFeature: false,
};
componentWillMount () {
this.fetchData();
}
/* Assign received prop to state, so that this state can be used in render */
componentWillReceiveProps(nextProps) {
if (this.props.isFetching && !nextProps.isFetching) {
this.state.enableFeature = nextProps.enableFeature;
}
}
fetchData () {
const { token } = this.props;
this.props.actions.fetchData(token)
}
handleEnableFeatureChange = (event) => {
this.setState({ enableFeature: event.target.checked })
};
render () {
return (<div>
{ this.props.isFetching && "Loading..." }
{
!this.props.isFetching && <label>
Enable Feature
<input
type="checkbox"
className="form-control"
checked={this.state.enableFeature}
onChange={this.handleEnableFeatureChange}
/>
</label>
}
</div>);
}
}
Note: The above code was not executed, but should work (babel's stage-0 code)
Change it to checked={this.state.enableFeature}

Categories

Resources