I'm actually having an issue in my React-native logic and I need your precious help !
I have my main component 'Main.js' and I want to import a function from 'Function.js' that should change the state of 'Main.js'...
But of course I don't have access to "Main.js"'s this.
So my question is:
How can I change the Main.js state from an exported function in Function.js ?
Here is the kind of code in Function.js
_function = () => {
... Getting MyState in AsyncStorage ...
var MyState = ...;
this.setState({ User: MyState })
}
And my Main.js
import { _function } from 'Function.js'
...
componentDidMount(){
this._function()
}
...
Either make the instance a parameter of the function:
export const _function = (main) => {
// ... Getting MyState in AsyncStorage ...
var MyState = …
main.setState({ User: MyState })
};
import { _function } from 'Function.js'
…
componentDidMount(){
_function(this)
}
…
Or don't use an arrow function so that you can actually use it as a method:
export function _function() {
// ... Getting MyState in AsyncStorage ...
var MyState = …
this.setState({ User: MyState })
};
import { _function } from 'Function.js'
…
componentDidMount(){
_function.call(this)
}
…
You could even install it as a real method:
import { _function } from 'Function.js'
class Main {
componentDidMount(){
this.method();
}
}
Main.prototype.method = _function;
You can pass a function in your _function parameters.
In main.js, you would have a function
setUser = (User) => {
this.setState({user});
}
Then you call _function and pass setUser as a parameter.
componentDidMount(){
_function(this.setUser)
}
In Function.js, you edit your function to receive the function and call it.
_function = (setUser) => {
... Getting MyState in AsyncStorage ...
var MyState = ...;
setUser(MyState);
}
Related
In a plain js file the code looks like
export default async function exportData() {
const { data } = await store
.dispatch('fetchData')
const { bookings } = data
const booking = bookings.length ? bookings[0]._id : ''
const event = {
bookingID: booking
}
// other methods and variables
return {
.....
}
}
inside the vue file
import exportData from './exportData'
export default {
setup() {
const {
fetchEvents,
isEventActive,
} = exportData()
fetchEvents()
}
}
the problem is in vue components the values from exportData gets undefined, gets error fetchEvents is not a function when the export is asynchronous. works well if not asynchronous. what is the workaround here??
You can try to declare fetchEvents,isEventActive methods in plan js file without wrapping it inside any function
const fetchEvents = () => {
//body
};
const isEventActive = () => {
//body
};
and export them as
export {fetchEvents, isEventActive};
now use them
import {fetchEvents,isEventActive} from 'path-to-js-file'
export default {
setup() {
fetchEvents()
isEventActive()
}
}
I have a function update() in WebContextProvider, from which I want to call another function updateAgain() which is also present in WebContextProvider. Below is the code for reference.
import React, { createContext, Component } from 'react';
export const WebContext = createContext();
class WebContextProvider extends Component {
state = {
someState: 1,
};
render() {
return (
<WebContext.Provider
value={{
data: ...this.state,
update: () => {
//call updateAgain() from here
},
updateAgain:() => {
//call this from update()
}
}}
>
{this.props.children}
</WebContext.Provider>
);
}
}
export default WebContextProvider;
You can declare the functions above the class declaration and provide them inside the value of the context provider, or if you need access to the state you'll have to define them inside your class and send references to the methods.
External functions example:
import React, { createContext, Component } from 'react';
export const WebContext = createContext();
const update = () => { /* Do something, you can call here updateAgain() */ };
const updateAgain = () => { /* Do something else */ };
export default class WebContextProvider extends Component {
state = {
someState: 1,
};
render() {
return (
<WebContext.Provider
value={{
data: ...this.state,
update,
updateAgain
}}>
{this.props.children}
</WebContext.Provider>
);
}
}
Example with class methods, when you need to use state:
import React, { createContext, Component } from 'react';
export const WebContext = createContext();
export default class WebContextProvider extends Component {
state = {
someState: 1,
};
render() {
return (
<WebContext.Provider
value={{
data: ...this.state,
update: this.update,
updateAgain: this.updateAgain
}}>
{this.props.children}
</WebContext.Provider>
);
}
update = () => { /* Do something, you can call here this.updateAgain() or use this.state */ }
updateAgain = () => { /* Do something else, you can use this.state here */ }
}
You can use this.propertyName to refer to any property on the object instance provided that you use a regular function instead of an arrow function.
const ctxObject = {
first: () => {
console.log("first");
},
second: function() {
console.log("second");
this.first();
}
}
ctxObject.second();
I have set up correctly according to doc redux boilerplate.
I have a folder of various action files which contains actions.
In this folder I have created an index.js where I am exporting all these files. I import it as named export.
When I console log the function itself it is there, but when I console log out this.props its not present.
I have tried to console log out the function outside the class its undefined.
If I import the action directly from the file it is defined in it works.
actions/formActions.js
export const formInput = (key, val) => (
{
type: FORM_INPUT,
payload: { key, val }
}
);
actions/index.js
export * from './formActions';
FormComp.js
import { formInput } from './actions'; // <-- this.props.formInput = undefined
or
import { formInput } from './actions/formActions'; // <-- this.props.formInput = func
connect:
class InputField extends Component { ... }
const FormComp = connect(({ forms }) => ({ forms }), { formInput })(InputField);
export { FormComp };
edit1: If I inside componentWillMount() console.log(formInput) //without this.props its there.
edit2 (solution?):
I was able to map actions to props with bindActionCreators. How ever I don't understand why I need to use bindActionCreators and why I can't just export connect as it is with actions as second param.
const FormComp = connect(
({ forms }) => ({ forms }),
dispatch => bindActionCreators({ formInput }, dispatch)
)(InputField);
The reason this.props.formInput is not defined but formInput is defined is because the redux connect is not properly setting the function formInput against the variable(property) formInput within the component props.
formInput is the function you are importing,
this.props.formInput is the property that should "point" to the function you import.
Try (I have separated out for clarity)
function mapStateToProps(state) {
return {
forms: state.forms
}
}
function mapDispatchToProps(dispatch) {
return {
formInput: (key, val) => dispatch(formInput(key val))
};
}
export connect(
mapStateToProps,
mapDispatchToProps
)(InputField);
Then anywhere in your code you call
this.props.formInput(key, val);
This will call the action via props, and therefore will dispatch. properly
You need to return a function which has dispatch and getState as parameter.
export const formInput = (key, val) => (dispatch, getState) => {
// maybe additional stuff
dispatch({
type: FORM_INPUT,
payload: { key, val }
});
};
See title Action Creators from the documents: https://redux.js.org/docs/basics/Actions.html
I'm trying to understand mobx implementation in React. I used create react app and update default configuration to use decorators. Then I created a simple store like this :
EDIT : after Ben Hare (thanks to him !) reply I updated my code like this :
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import MessageStore from "./store/messages";
ReactDOM.render(<App store={new MessageStore()} />,
document.getElementById('root'));
** App.js **
import React from "react";
import { observer } from "mobx-react";
#observer
export default class App extends React.Component {
constructor(props) {
super(props);
this.store = props.store;
}
render() {
return <ul>
{ this.store.allMessages.map((msg) => {
return <li key={msg}>{msg}</li>
})}
</ul>
}
}
messages.js
import {action, observable, computed} from "../../node_modules/mobx/lib/mobx";
export default class MessageStore {
#observable messages = ["My first message"];
constructor() {
setInterval(() => {
// Add some random messages every second
this.addMessage(Math.random());
}, 1000);
}
#action addMessage(msg) {
this.messages.push(msg);
}
#computed get allMessages() {
return this.messages;
}
}
The first message is displayed, but component never update when setInterval add message into the store. Can you help me ?
Works for me:
https://codesandbox.io/s/LgQXNBnNW
Did you see any errors in the browser log or terminal?
Check my approach please. Maybe it will help:
MobX store:
import { action, observable, runInAction } from 'mobx'
class DataStore {
#observable data = null
#observable error = false
#observable fetchInterval = null
#observable loading = false
//*Make request to API
#action.bound
fetchInitData() {
const response = fetch('https://poloniex.com/public?command=returnTicker')
return response
}
//*Parse data from API
#action.bound
jsonData(data) {
const res = data.json()
return res
}
//*Get objects key and push it to every object
#action.bound
mapObjects(obj) {
const res = Object.keys(obj).map(key => {
let newData = obj[key]
newData.key = key
return newData
})
return res
}
//*Main bound function that wrap all fetch flow function
#action.bound
async fetchData() {
try {
runInAction(() => {
this.error = false
this.loading = true
})
const response = await this.fetchInitData()
const json = await this.jsonData(response)
const map = await this.mapObjects(json)
const run = await runInAction(() => {
this.loading = false
this.data = map
})
} catch (err) {
console.log(err)
runInAction(() => {
this.loading = false
this.error = err
})
}
}
//*Call reset of MobX state
#action.bound
resetState() {
runInAction(() => {
this.data = null
this.fetchInterval = null
this.error = false
this.loading = true
})
}
//*Call main fetch function with repeat every 5 seconds
//*when the component is mounting
#action.bound
initInterval() {
if (!this.fetchInterval) {
this.fetchData()
this.fetchInterval = setInterval(() => this.fetchData(), 5000)
}
}
//*Call reset time interval & state
//*when the component is unmounting
#action.bound
resetInterval() {
if (this.fetchInterval) {
clearTimeout(this.fetchInterval)
this.resetState()
}
}
}
const store = new DataStore()
export default store
Have problem with state in my component.
I'm trying to get status from my reducer but state is empty just getting undefined
Here is my actionCreator
export function checkLogin() {
return function(dispatch){
return sessionApi.authCheck().then(response => {
dispatch(authSuccess(true));
}).catch(error => {
throw(error)
})
}
}
My reducer
export const authStatus = (state = {}, action) => {
switch(action.type){
case AUTH_FALSE:
return{
status: action.status
}
case AUTH_TRUE:
return {
...state,
status: action.status
};
default:
return state;
}
};
And here is my component where i'm trying to get state
const mapStateToProps = (state) => {
return {
status: state.status
}
};
const mapDispatchToProps = (dispatch:any) => {
const changeLanguage = (lang:string) => dispatch(setLocale(lang));
const checkAuth = () => dispatch(checkLogin());
return { changeLanguage, checkAuth }
};
#connect(mapStateToProps, mapDispatchToProps)
I need to get status from the state
Component
import * as React from "react";
import Navigation from './components/navigation';
import { connect } from 'react-redux';
import { setLocale } from 'react-redux-i18n';
import cookie from 'react-cookie';
import {checkLogin} from "./redux/actions/sessionActions";
class App extends React.Component<any, any> {
constructor(props:any) {
super(props);
this.state = {
path: this.props.location.pathname
};
}
componentDidMount(){
this.props.checkAuth();
this.props.changeLanguage(cookie.load('lang'));
}
componentWillUpdate(){
}
render() {
return (
<div>
<Navigation path={this.state.path} />
{this.props.children}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
status: state.status
}
};
const mapDispatchToProps = (dispatch:any) => {
const changeLanguage = (lang:string) => dispatch(setLocale(lang));
const checkAuth = () => dispatch(checkLogin());
return { changeLanguage, checkAuth }
};
#connect(mapStateToProps, mapDispatchToProps)
export class Myapp
extends App {}
You cannot access props that are asynchronous inside of the constructor. As the constructor will be executed only once, when you instantiate your component. When you instantiate your component your asynchronous call has not responded yet, therefore this.props.status is undefined.
You could use componentWillReceiveProps from React lifecycle methods for example:
componentWillReceiveProps(nextProps) {
console.log(nextProps.status);
}
This method will be executed everytime a prop connected, or passed, to the component will change.
You could also use this.props.status inside of the render as this one is also executed everytime a prop changed.
For a better understanding of react lifecycle you could have the look at the different methods available, here : https://facebook.github.io/react/docs/react-component.html