Redux Simple Router pushPath not updating URL - reactjs

I ported redux-simple-router into a boilerplate react/redux isomorphic kit (https://github.com/erikras/react-redux-universal-hot-example). I have a simple click event handler that calls 'pushPath' from redux-simple-router. However, pushPath doesn't seem to update my URL. I already implemented the initial port (syncReduxAndRouter) and other routes seem to work fine (other routes use updatePath). Is there something else I need to do to get this to work?
import React, {Component} from 'react';
import { pushPath } from 'redux-simple-router';
import {connect} from 'react-redux';
#connect(null,
{ pushPath })
export default class MyContainer extends Component {
constructor() {
super();
this.state = { links: [{key: 0, name: 'Link1'}, {key: 1, name: 'Link2'}, {key: 2, name: 'Link3'}] };
}
// pass in redux actions as props
handleClick(value) {
pushPath('/Links/' + value);
}
render() {
return (
<div className="container">
<div>Search bar here</div>
<div className={styles.tile_container}>
Tiles here
{this.state.links.map(source =>
<div name={link.name} key={link.key} className={styles.source_tile} onClick= {this.handleClick.bind(this, link.name)}>{link.name}</div>
)}
</div>
</div>
);
}
}
Here's the version of my code with the fix. I needed to use an instance of redux-simple-router that was connected to the store and then pass its methods to the component as a prop.
import React, {Component, PropTypes} from 'react';
import { pushPath } from 'redux-simple-router';
import {connect} from 'react-redux';
#connect(null,
{ pushPath })
export default class MyComponent extends Component {
static propTypes = {
pushPath: PropTypes.func.isRequired
};
constructor() {
super();
this.state = { links: [{key: 0, name: 'Link1'}, {key: 1, name: 'Link2'}, {key: 2, name: 'Link3'}] };
}
// pass in redux actions as props
handleClick(value) {
this.props.pushPath('/Links/' + value);
}
render() {
return (
<div className="container">
<div>Search bar here</div>
<div className={styles.tile_container}>
Tiles here
{this.state.links.map(source =>
<div name={link.name} key={link.key} className={styles.source_tile} onClick= {this.handleClick.bind(this, link.name)}>{link.name}</div>
)}
</div>
</div>
);
}
}

You are calling action creator pushPath instead of the bound method.
Action creator just returns plain object. To call bound method, you should do
handleClick(value) {
this.props.pushValue('/Links/' + value);
}
#connect create proper methods for dispatching and propagate it to you via props.

Related

How can I refer to another react component?

is there a way to get component's state name, from another component?
I have a component Chat:
import ChatMessage from './ChatMessage'
class Chat extends Component {
state = {
name: 'Bob',
messages: [],
}
getStateName = function(){
return this.state.name
}
}
and another component ChatMessage:
import Chat from './Chat'
class ChatMessage extends Component{
render(){
return(
<p> {Chat.getStateName} </p>
)
}
}
I would like to get as a result 'Bob'.
I was thinking of using a function called getStateName, but it doesn't seem to work. Any idea's how I can fix that?
Ideally state is private and fully controlled by the component. Redux is a library that can be used to manage application wide states via an application store. Refer to redux's getting started to set your app up.
You are on right track to use Components, but you need some changes to look into
here is Stackblitz link for your project
your Chat.js component file will be
import React, { Component } from 'react';
class Chat extends Component {
state = {
name: 'Bob',
messages: []
};
render() {
return <p>{this.state.name}</p>;
}
}
export default Chat;
And ChatMessage.js component will be
import React, { Component } from 'react';
import Chat from './Chat';
class ChatMessage extends Component {
render() {
return <Chat />;
}
}
export default ChatMessage;
Here's a solution that uses a plain ES6 class to store user data, and delegates rendering to a React component.
Either create an instance of the class before accessing it:
class User {
state = {
name: "Bob",
messages: [
"Could you get cucumber from the grocery store?",
"Don't forget the tomatoes, too!",
"Scrap the tomatoes. No tomatoes.",
"Honey, let's get take away instead."
]
};
}
const user1 = new User(); // ← initialising the object instance
const App = () => (
<>
<h1> {user1.state.name} </h1>
<ul>
{user1.state.messages.map((message) => (
<li key={message}>{message}</li>
))}
</ul>
</>
);
export default App;
Or make the state static, which allows you to access it without initialisation:
class User {
static state = { // ← the state object is now static
name: "Bob",
messages: [
"Could you get cucumber from the grocery store?",
"Don't forget the tomatoes, too!",
"Scrap the tomatoes. No tomatoes.",
"Honey, let's get take away instead."
]
};
}
// no need for initialisation
// const user1 = new User();
const App = () => (
<>
<h1> {User.state.name} </h1>
<ul>
{User.state.messages.map((message) => (
<li key={message}>{message}</li>
))}
</ul>
</>
);
export default App;

Having trouble rendering data in react component

I'm trying to render the following the 'dogName' value of the following array to the browser, but it's coming up as 'undefined':
[
{
"id": 1,
"dogName": "bruce"
},
{
"id": 2,
"dogName": "borker"
},
{
"id": 3,
"dogName": "henry"
}
]
So, first of all, the data is pulled from a database and set in state in the parent component, where's it's passed as props to the child component 'DogNameList' (which I've trimmed down to just the relevant bits):
import React from 'react';
import './styles.css'
import DogList from './DogList'
import Dogue from './Dogue'
import axios from 'axios'
import DogNameList from './DogNameList'
class App extends React.Component {
constructor(){
super()
this.state = {
**dogName:[]**
}
}
componentDidMount() {
axios.get('http://localhost:3000/dogs')
.then(res => {
this.setState({
**dogName:res.data**
})
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div>
<DogNameList **names = {this.state.dogName}**/>
<Dogue/>
</div>
);
}
}
export default App;
In DogNameList, the data is mapped over and then passed as props to the 'Dogue' component (stupid names, I know, but this is a personal project):
import React from 'react'
import Dogue from './Dogue'
const DogNameList = (props) => {
return(
<div>
{
props.names.map(name => {
console.log(name.dogName)
return <Dogue name = {name} key ={name.id}/>
})
}
</div>
)
}
export default DogNameList
finally, it's supposed to be rendered to the browser via the 'Dogue' component:
import React from 'react'
import axios from 'axios'
class Dogue extends React.Component {
constructor(props){
super(props)
this.state = {
}
}
render(){
return (
<div>
<img className = 'img' src = {this.props.dogList}/>
<br/>
<form className = 'form'>
<input type = 'text' placeholder = 'Enter dog name'/>
<br/>
<button>Submit</button>
</form>
**<h2>dog name: {this.props.name}</h2>**
</div>
)
}
}
export default Dogue
Any ideas why it's not working? I console logged the following and it returned the list of names (not as strings, I should add):
props.names.map(name => {
console.log(name.dogName)
First of all, replace this
<h2>dog name: {this.props.name}</h2>
with this
<h2>dog name: {this.props.name.dogName}</h2>
because you are creating a component with object, so name property actually holds the object, not the name property of the object.
return <Dogue name = {name} key ={name.id}/>
You also don't declare somewhere this property
{this.props.dogList}
Also to handle the undefined error messages, do this
{this.state.dogName && <DogNameList names ={this.state.dogName}/>}

MobX componentWillReact not firing

I read in the docs that mobx react provides a new lifecycle called componentWillReact. However, it seems that my class only reacts to mobx changes in the render function. componentWillReact is never triggered when my store changes.
I am sending "next" down as a prop. This app does not make use of mobx inject.
import { observer } from 'mobx-react';
#observer class QuickShopNew extends Component {
componentWillReact() {
console.log(this.props.store.next);
}
render(){
//console.log(this.props.store.next);
return(
<div>
</div>
)
}
}
As I can see your component doesn't dereference observed property in the render method. That's why mobx doesn't know that component should be rerendered and componentWillReact should be called on value change.
You can read how observer component work here
And here is simple working example on codepen
const { Component, PropTypes } = React;
const { observable } = mobx;
const { observer } = mobxReact;
// Store for state
class Store {
#observable next = 0;
increaseNext = () => this.next +=1;
}
let store = new Store();
#observer
class MyComponent extends Component {
componentWillReact() {
console.log(this.props.store.next);
}
render() {
return (
<div>
<h1>{this.props.store.next}</h1>
</div>
);
}
}
class App extends Component {
render() {
return (
<div>
<MyComponent
store={store}
/>
<button onClick={store.increaseNext}>
Increase
</button>
</div>
);
}
}
// Insert into container
ReactDOM.render(<App />, document.getElementById('app'));
I think that you should avoid of using the "componentWillReact" and use just standart Mobx services like this example is showing :
If you intended to update the observable variable with action then use computed method to send updated value into UI.
import React from 'react';
import { observable, action, computed } from 'mobx';
import { observer } from 'mobx-react';
class AppStore {
#observable next = 0;
#action updateNext = () => this.next = this.next + 1;
#computed get UI_renderValueNext() {
return this.next ? this.next : 0;
}
}
const appStore = new AppStore();
#observer
class AppComponent extends React.Component {
render(){
return (
<div>
<div>
{this.props.UI_rennderNext}
</div>
<button onClick={this.props.updateNext}>Click ME</button>
</div>
)
}
}
ReactDOM.render(
<AppComponent />, document.getElementById('root')
)

React - passing object through props

I am new to React and trying to pass object through attributes but getting following error.
Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {title}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of MeetingComponent.
Here is my code:
Main.jsx
import React from 'react';
import MeetingComponent from '../components/Meeting.jsx';
let meeting = {
title: 'Some title'
};
class AppComponent extends React.Component {
render() {
return (
<div className="index">
<MeetingComponent dataMeeting={meeting} />
</div>
);
}
}
AppComponent.defaultProps = {};
export default AppComponent;
Meeting.jsx
import React from 'react';
class MeetingComponent extends React.Component {
render() {
return (
<div>{this.props.dataMeeting}</div>
);
}
}
MeetingComponent.defaultProps = {};
export default MeetingComponent;
How can I solve this? What is the best practice?
The problem is here
<div>{this.props.dataMeeting}</div>
You cannot render an object in React, maybe you were trying to do
<div>{this.props.dataMeeting.title}</div>
If you want to pass properties of an object , it can be done as follows:
<MeetingComponent {...meeting} />
To access title of the meeting object inside the Meeting component, you can do it by calling it directly this.props.title
You can render it as <div>{this.props.title}</div>
i've used this method to pass object through component
let meeting = {
title: 'Some title'
};
class AppComponent extends React.Component {
render() {
const jsonData =JSON.stringify(meeting);
return (
<div className="index">
<MeetingComponent dataMeeting={jsonData } />
</div>
);
}
}
class MeetingComponent extends React.Component {
render() {
const data = JSON.parse(this.props.dataMeeting);
return (
<div>{data}</div>
<div>{data.title}</div>
);
}
}
static propTypes = {
pieChartSortSettings: PropTypes.shape({
type: PropTypes.string.isRequired,
order: PropTypes.string.isRequired,
}),
};
Here is a example how to pass object in props. You can also checkout methods like: PropTypes.arrayOfand PropTypes.objectOf(more: https://reactjs.org/docs/typechecking-with-proptypes.html)
Best practice is make your props as plain as possible, in your case it may be
<MeetingComponent title={ meeting.title } />
class MeetingComponent extends React.Component {
propTypes = {
title: React.PropTypes.string.isRequired
}
render() {
return (
<div>title: { this.props.title }</div>
);
}
}
And always use propTypes for better warning messages

Decoupling React Components and Redux Connect

As seen here I am trying to decouple my app's components as much as I can and make them not aware of any storage or action creator.
The goal is to have them to manage their own state and call functions to emit a change. I have been told that you do this using props.
Considering
// Menu.jsx
import React from 'react'
import { className } from './menu.scss'
import Search from 'components/search'
class Menu extends React.Component {
render () {
return (
<div className={className}>
<a href='#/'>Home</a>
<a href='#/foo'>foo</a>
<a href='#/bar'>bar</a>
<Search />
</div>
)
}
}
And
// Search.jsx
import React from 'react'
import { className } from './search.scss'
class Search extends React.Component {
render () {
let { searchTerm, onSearch } = this.props
return (
<div className={`search ${className}`}>
<p>{searchTerm}</p>
<input
type='search'
onChange={(e) => onSearch(e.target.value)}
value={searchTerm}
/>
</div>
)
}
}
Search.propTypes = {
searchTerm: React.PropTypes.string,
onSearch: React.PropTypes.function
}
export default Search
And reading here I see a smart use of Provider and connect and my implementation would look something like this:
import { bindActionCreators, connect } from 'redux'
import actions from 'actions'
function mapStateToProps (state) {
return {
searchTerm: state.searchTerm
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators({
dispatchSearchAction: actions.search
}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Search)
Assuming I have a store handling searchTerm as part of the global state.
Problem is, where does this code belongs to? If I put it in Search.jsx I will couple actions with the component and more important to redux.
Am I supposed to have two different versions of my component, one decoupled and one connect()ed and have <Menu /> to use it? If yes what would my files tree look like? One file per component or a like a make-all-connected.js ?
In redux, exist a new kind of component that is called containers, this is the component that use connect(mapStateToProps, mapActionsToProps), to pass the state and actions to the current component.
All depends of the use of the component. For example, if you component Search only going to be use with the same state and action, You container could be the same that your component like this:
// Search.jsx
import { connect } from 'redux'
import actions from 'actions'
import React from 'react'
import { className } from './search.scss'
class Search extends React.Component {
render () {
let { searchTerm, onSearch } = this.props
return (
<div className={`search ${className}`}>
<p>{searchTerm}</p>
<input
type='search'
onChange={(e) => onSearch(e.target.value)}
value={searchTerm}
/>
</div>
)
}
}
Search.propTypes = {
searchTerm: React.PropTypes.string,
onSearch: React.PropTypes.function
}
function mapStateToProps ({searchTerm}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
onSearch: actions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Search)
But if your plan is reuse this component in another containers and the searchTerm or the action are different on the global state. The best way is passing this properties through other containers, and keep the Search component pure. Like this:
// Container1.jsx
import { connect } from 'redux'
import actions from 'actions'
import React, { Component } from 'react'
class Container1 extends Component {
render() {
const { searchTerm, handleOnSearch } = this.props;
return (
<div>
<Search searchTerm={searchTerm} onSearch={handleOnSearch} />
</div>
)
}
}
function mapStateToProps ({interState: {searchTerm}}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
handleOnSearch: actions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Container1)
// Container2.jsx
import { connect } from 'redux'
import otherActions from 'otheractions'
import React, { Component } from 'react'
class Container2 extends Component {
render() {
const { searchTerm, handleOnSearch } = this.props;
return (
<div>
<Search searchTerm={searchTerm} onSearch={handleOnSearch} />
</div>
)
}
}
function mapStateToProps ({otherState: {searchTerm}}) {
return {
searchTerm
};
}
const mapDispatchToProps = {
handleOnSearch: otherActions.search
}
export default connect(mapStateToProps, mapDispatchToProps)(Container2)
For more information, read the official docs about using redux with react.

Resources