i try do that Store objet
UserStore.js
import { observable, action } from 'mobx';
class UserStore {
constructor() {
const me = observable({
me: null,
auth: action.bound(function(me) {
this.me = me;
})
})
}
}
export default UserStore;
After this, i do that
App.js
const App = inject('routing','UserStore')(observer(class App extends Component {
constructor(props, context) {
super(props, context);
this.handleFiles = this.handleFiles.bind(this);
this.prepareTable = this.prepareTable.bind(this);
this.state = {excel: null};
}
render() {
const {location, push, goBack} = this.props.routing;
const {userStore} = this.props.userStore;
And in index.js i do
const stores = {
// Key can be whatever you want
routing: routingStore,
UserStores
};
const history = syncHistoryWithStore(browserHistory, routingStore);
ReactDOM.render(
<Provider {...stores}>
<Router history={history}>
<Entry/>
</Router>
</Provider>,
document.getElementById('root')
);
Lets, after all of this i try open the localhost:3000 and se this error
Error: MobX observer: Store 'UserStore' is not available! Make sure it is provided by some Provider
UPDATE:
I'm create a project with create-react-app, and i can't use # in code(example for #injector)
I think you should return an instance of the store.
To be a bit more organized I have a file like "storeInitializer.ts" (using typescript here):
import YourStoreName from '../yourStoreFolder/yourStore';
export default function initializeStores() {
return {
yourStoreName: new YourStoreName(),
}
}
Then I have another file like "storeIdentifier.ts":
export default class Stores {
static YourStoreName: string = 'yourStoreName';
}
In the app file I do something like this:
import React from 'react';
import { inject, observer } from 'mobx-react';
import Stores from './storeIdentifier';
const App = inject(Stores.YourStoreName)(observer(props: any) => {
//code here
});
export default App;
or if you are using the class component approach...
import React from 'react';
import { inject, observer } from 'mobx-react';
#inject(Stores.YourStoreName)
#observable
class App extends Component {
//code here
}
export default App;
Related
I have created a small application and connected it to Redux. Unfortunately when creating new components and using the same exact code those new components cannot seem to connect to redux and get undefined when accessing it (using mapStateToProps).
I have tried to create new Components and connect them again to no avail. I'm kind of at loss as to why it isn't working especially since the rest of the application can connect and get the state properly
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store} >
<App />
</Provider>
, document.getElementById('root'));
store.js:
const initialState = {
guessedTimezone: '',
timezone: '',
pseudo: '',
};
function rootReducer(state = initialState, action) {
console.log(action);
if (action.type === 'CHANGE_TIMEZONE') {
return Object.assign({}, state, {
timezone: action.timezone,
guessedTimezone: action.guessedTimezone
})
}
if (action.type === 'CHANGE_PSEUDO') {
return Object.assign({}, state, {
pseudo: action.pseudo,
token: action.token
})
}
return state;
}
export default rootReducer;
new Component not connecting:
import React, { Component } from 'react'
import { connect } from 'react-redux'
export class TestPseudo extends Component {
render() {
console.log(this.props.pseudo);
return (
<div>
{this.props.pseudo}
</div>
)
}
}
const mapStateToProps = state => {
return {
pseudo: state.pseudo
}
}
export default connect(mapStateToProps)(TestPseudo)
Here for example this.props.pseudo returns undefined when, if the connection happens, it should return the value if i understand it correctly and yet it shows undefined
EDIT:
App.js as per requested :
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Homepage from './Components/Homepage';
import moment from 'moment';
import moment_timezone from 'moment-timezone';
import HeaderApp from './Components/HeaderApp';
import { TestPseudo } from './Components/TestPseudo';
export class App extends Component {
async componentDidMount() {
let tz = moment.tz.guess(true);
let date = moment(new Date()).local();
let timezone = date['_i'].toString().split('(')[1].split(')')[0];
this.props.dispatch({
type: 'CHANGE_TIMEZONE',
guessedTimezone: tz,
timezone: timezone
})
console.log(`Guessed timezone: ${tz} (${timezone})`);
}
_showHomepage() {
if (this.props.showHomepage && this.props.loaded) {
return (
<div style={styles.mainWindow}>
{/*<Homepage click={this._handleClick} />*/}
<TestPseudo />
</div>
)
}
}
_showHeader() {
return (
<div>
<HeaderApp />
</div>
)
}
render() {
return (
<div>
{this._showHeader()}
{this._showHomepage()}
</div>
)
}
}
const styles = {
mainWindow: {
height: '100vh',
width: '100vw'
}
}
const mapStateToProps = state => {
return {
guessedTimezone: state.guessedTimezone,
timezone: state.timezone,
};
};
export default connect(mapStateToProps)(App);
I call that new Component instead of my old Component. The homepage can connect but not the new one so i think it's not a problem of emplacement
I think its here
import { TestPseudo } from './Components/TestPseudo';
You are importing the non-connected component. Try this
import TestPseudo from './Components/TestPseudo';
For your understanding, exporting as default can be imported like so;
export default Component
import WhateverName from ....
Named export like const or in your case class;
export class Component
import { Component } from ...
So use brackets when Named, and skip brackets when default.
I'm setting up ContextApi for the first time in a production app, hoping to replace our current handling of our app configs with it. I've followed the official docs and consulted with similar issues other people are experiencing with the API, and gotten it to a point where I am able to correctly the config when I do Config.Consumer and a callback in render functions. However, I cannot get this.context to return anything other than an empty object.
Ideally, I would use this.context in lifecycle methods and to avoid callback hell, so help would be appreciated. I've double checked my React version and that I'm setting the contextType. Below is a representation of the code
config.js
import { createContext } from "react";
export default createContext();
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { Router, browserHistory } from "react-router";
import { syncHistoryWithStore } from "react-router-redux";
import Config from "../somePath/config";
// more imports
function init() {
const config = getConfig();
const routes = getRoutes(config);
const history = syncHistoryWithStore(browserHistory, appStore);
ReactDOM.render(
<Provider store={appStore}>
<Config.Provider value={config}>
<Router history={history} routes={routes} />
</Config.Provider>
</Provider>,
document.getElementById("app")
);
}
init();
someNestedComponent.js
import React, { Component } from "react";
import { connect } from "react-redux";
import Config from "../somePath/config";
#connect(
state => ({
someState: state.someState,
})
)
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.context);
}
render() {
return (...someJSX);
}
}
someNestedComponent.contextType = Config;
export default someNestedComponent;
Currently running on:
React 16.8.6 (hopi to see error messages about circuitous code but
didn't get any warnings)
React-DOM 16.7.0
React-Redux 6.0.1
The problem is that someNestedComponent doesn't refer to the class where this.context is used:
someNestedComponent.contextType = Config;
It refers to functional component that wraps original class because it was decorated with #connect decorator, it is syntactic sugar for:
const someNestedComponent = connect(...)(class someNestedComponent extends Component {
...
});
someNestedComponent.contextType = Config;
Instead, it should be:
#connect(...)
class someNestedComponent extends Component {
static contextType = Config;
componentDidMount() {
console.log(this.context);
}
...
}
There are no callback hell problems with context API; this is conveniently solved with same higher-order component pattern as used in React Redux and can also benefit from decorator syntax:
const withConfig = Comp => props => (
<Config.Consumer>{config => <Comp config={config} {...props} />}</Config.Consumer>
);
#connect(...)
#withConfig
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.props.config);
}
...
}
You didn't use a consumer to get the values
ref: https://reactjs.org/docs/context.html#contextconsumer
I'm trying to export a redux project as a node_module that has an index.js shown below (simplified):
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import promiseMiddleware from 'redux-promise-middleware';
import App from './App.jsx';
const middlewares = [thunk.withExtraArgument(), promiseMiddleware()];
const middlewareEnhancer = applyMiddleware(...middlewares);
const preloadedState = {};
const store = createStore(
rootReducer,
preloadedState,
middlewareEnhancer
);
const ExampleModule = (props) => {
return (
<Provider store={store}>
<App />
</Provider>
);
};
export default ExampleModule;
In my main application:
...
import ExampleModule from 'example-module';
class Application extends React.Component {
render() {
return <ExampleModule />;
}
}
function mapStateToProps({ state }) {
return {
state: state
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(require('..').actions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Application);
This throws an error:
bundle.js:349 Uncaught Invariant Violation: Could not find "store" in either the context or props of "Connect(App)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(App)"
I'm assuming it's because this essentially creates nested <Providers> which is against Redux's methodology of one store.
My question would be what would be the best way to go about publishing a node_module that has a redux store in it?
Found the answer here:
https://redux.js.org/recipes/isolating-redux-sub-apps
It keeps the store local to the component.
In the js file below we create an integer(ttSelectedItem).
How do you use it on another .js file ?
(Without clicking any button)
Is AsyncStorage solving that problem? If it is true, how?
import React, { Component } from 'react';
import {Platform,StyleSheet,Text,View,Image,ImageBackground} from 'react-native';
import Picker from 'react-native-wheel-picker'
var PickerItem = Picker.Item;
var numberList = [];
var ttSelectedItem,
for (let i = 0; i < 41; i++){
numberList.push(i.toString());
}
export default class yks extends Component<{}> {
constructor(props) {
super(props);
this.state = {
ttSelectedItem : 20,
itemList: numberList,
};
}
onPickerSelect (index, selectedItem) {
this.setState({
[selectedItem] : index,
})
}
render () {
return (
<View>
<Picker style={{width: "100%", height: "100%"}}
selectedValue={this.state.ttSelectedItem}
onValueChange={(index) => this.onPickerSelect(index, 'ttSelectedItem')}>
{this.state.itemList.map((value, i) => (
<PickerItem label={value} value={i} key={"money"+value}/>
))}
</Picker>
</View>
);
}
}
You can create a file ttSelectedItem.js and import it in all the components you need.
Example:
//ttSelectedItem.js
const ttSelectedItem = 'Hello';
export default ttSelectedItem
//YourComponent.js
import ttSelectedItem from './path-to-ttSelectedItem';
class YourComponent extends React.Component {
console.log(ttSelectedItem); // print Hello
}
More info: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export
You can also pass down the prop from a parent component to its children.
Example:
// App.js
import FirstComponent from 'path-to-first-component';
import SecondComponent from 'path-to-second-component';
class App extends React.Component {
render() {
return (
<View>
<FirstComponent ttSelectedItem={'Hello'} />
<SecondComponent ttSelectedItem={'Hello'} />
</View>
)
}
}
// FirstComponent.js
class FirstComponent extends React.Component {
console.log(this.props.ttSelectedItem) //print Hello
}
export default FirstComponent
// SecondComponent.js
const SecondComponent = (props) => {
console.log(props.ttSelectedItem) //print Hello
}
export default SecondComponent
Depending on how complex your code will be, you can use HOCs to wire up some data and pass down your components
Example:
//ttSelectedItem.js
const ttSelectedItem = (Component) => {
return <Component ttSelectedItem={'Hello'} />
}
export default ttSelectedItem;
//YourComponent.js
import ttSelectedItem from 'path-to-ttSelectedItem';
class YourComponent extends Component{
(...)
console.log(this.props.ttSelectedItem); //print Hello
(...)
}
export default ttSelectedItem(YourComponent);
More detail: https://reactjs.org/docs/higher-order-components.html
Or if you need an even complex code, you can use Redux Store to keep this data
Example using Redux and ReduxThunk:
//App.js
import ReduxThunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reducer from 'path-to-your-reducer';
import YourComponent from 'path-to-your-component';
class App extends Component {
render() {
const store = createStore(reducer, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<YourComponent />
</Provider>
);
}
}
// YourComponent.js
import { connect } from 'react-redux';
class YourComponent extends React.Component {
console.log(this.props.ttSelectedItem) // prints Hello
}
const mapStateToProps = function(state){
return {
ttSelectedItem: state.ttSelectedItem,
}
}
export default connect(mapStateToProps, {})(MainAppContainer)
// Reducer.js
const INITIAL_STATE = {
ttSelectedItem: 'Hello',
};
export default (state = INITIAL_STATE) => {
return state;
};
More info: https://redux.js.org/basics/store
The last example is just to show another way to handle data between components using Redux. It should be used only when dealing with really complex data sharing.
I'd suggest you to just follow the first example, it might be enough
Hope it helps
I am just beginning to play with react/redux. I just want to input some text and hit submit and then pass that to another component that will display whatever was input.
I know I can get the data from point A to B because if I use store.subscribe than I can access the state and it is always accurate. I am trying to use mapStateToProps though and I am not having any luck.
I am not using mapDispatchToProps so maybe that is an issue? I cant seem to find a good simple example. mapStateToProps also only seems to run when I refresh the page (using webpack-dev-server) since it only logs one time on page load and never again.
_______________ Input.js _________________
import React from 'react';
import store from '../redux/store';
import { connect } from 'react-redux';
import { addSearchParam } from '../redux/actions';
export default class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
player: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
player: event.target.value
});
}
handleSubmit(event) {
event.preventDefault();
store.dispatch(addSearchParam(this.state.player))
}
render() {
return ( <form onSubmit = {this.handleSubmit} >
<label>
<input type="text" value={this.state.player}
onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
_______________ Info.js _________________
import React from 'react';
import store from '../redux/store';
import { connect } from 'react-redux';
class Info extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<h2> {this.props.player}</h2>
)
}
}
function mapStateToProps(state) {
console.log("mapStateToPropsInfo: ", state)
return {
player: state.player
}
}
export default connect(mapStateToProps, null)(Info);
_______________ reducers.js _________________
'use strict';
import {
combineReducers
} from 'redux';
const SEARCH_PARAM = 'SEARCH_PARAM';
const searchReducer = (state = '', action) => {
if (action.type === SEARCH_PARAM) {
return action.text;
}
return state;
}
export const reducers = combineReducers({
searchReducer
})
export default reducers;
_______________ actions.js _________________
'use-strict';
export const addSearchParam = input => {
return {
type: 'SEARCH_PARAM',
id: 'player',
text: input
}
}
_______________ index.js _________________
'use-strict';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './js/App';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from './js/redux/reducers'
let store = createStore(reducers)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root')
);
Those seem to be the most important files related to this problem but I can provide more if necessary. Thanks for any help. Hopefully im just missing something simple.
I think the issue is that you have written actions but never used/connected it. You need to use mapDispatchToProps in Input.js.
First import the action in input.js like this:
import { addSearchParam } from './actions';
Write mapDispatchToProps function like this:
function mapDispatchToProps(dispatch){
return bindActionCreators({addSearchParam}, dispatch);
}
Then, inside Input.js in handleSubmit function do this:
this.props.addSearchParam(this.state.player)
Also, instead of exporting class while declearing, change export statement of Input.js to also bind the mapDispatchToProps :
export default connect(null, mapDispatchToProps)(Input);