My 'store.js' does the following:
export default function configureStore(initialState = {todos: [], floatBar: {} }) {
return finalCreateStore(rootReducer, initialState)
}
Then in my 'client.js', I have the intialized states, but didn't define 'todos' array, and set up the Router:
let initialState = {
floatBar: {
barStatus: false,
id: 0
}
}
let store = configureStore(initialState)
render(
<div>
<Provider store={store}>
<Router history={hashHistory}>
<Route
path="/"
component={App}
>
<Route
component={FirstPage}
path="firstpage"
/>
<Route
component={NewPage}
path="/newpage/:id"
/>
</Route>
</Router>
</Provider>
</div>,
document.getElementById('app')
)
Then in my 'item.js' component, which is a child of 'FirstPage.js', it gets an object 'todo' and retrieves the '.id', which is an object from the 'todos' object-array (inside the render() return{}), I have the following:
<Link to={`/newpage/${this.props.todo.id}`}>Link1</Link>
Lastly, in my newly linked page, 'NewPage.js', I want to be able to use the same exact 'todo' object in 'item.js', so I can call 'todo.id' and such. How can I do so?
Could anyone show the proper way to do this using redux react-router? Would really appreciate it.
**UPDATE
**NEWEST UPDATE for actions
actions.js has all of my action creators inside:
import * as actions from '../redux/actions'
class NewPage extends Component{
handleCommentChange(){
this.props.actions.updateComment()
}
render(){
return()
}
}
function mapDispatchToProps(dispatch){
return{
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(
mapDispatchToProps
)(NewPage);
You can access to "todo id" from props.params.id . Also you can access to props.params of NewPage through "ownProps" in "mapStateToProps"
import {connect} from "react-redux";
import React, { Component } from 'react'
import { Divider } from 'material-ui'
const styles = {
title:{
color: 'white',
textAlign: 'left',
marginLeft: 30
}
}
class NewPage extends Component{
render(){
return(
<div>
<div style={styles.title}>
<font size="4">
{this.props.todo.title}
</font>
</div>
<Divider style={{backgroundColor:'#282828'}}/>
<p style={{color: 'white'}}>{this.props.todo.detail}</p>
</div>
)
}
}
const mapStateToProps=(state, ownProps)=>{
let todo = state.todos.filter(todo=>todo.id==ownProps.params.id);
return{
todo:todo[0]
}};
export default connect(mapStateToProps)(NewPage);
Another approach, especially useful for dynamic props, is to clone the child component that gets injected as
props by React Router, which gives you the opportunity to pass additional props in the process.
example
class Repos extends Component {
constructor(){...}
componentDidMount(){...}
render() {
let repos = this.state.repositories.map((repo) => (
<li key={repo.id}>
<Link to={"/repos/details/"+repo.name}>{repo.name}</Link>
</li>
));
let child = this.props.children && React.cloneElement(this.props.children,
{ repositories: this.state.repositories }
);
return (
<div>
<h1>Github Repos</h1>
<ul>
{repos}
</ul>
{child}
</div>
);
}
}
////////////////////////////
class RepoDetails extends Component {
renderRepository() {
let repository = this.props.repositories.find((repo)=>repo.name === this.props.params.
repo_name);
let stars = [];
for (var i = 0; i < repository.stargazers_count; i++) {
stars.push('');
}
return(
<div>
<h2>{repository.name}</h2>
<p>{repository.description}</p>
<span>{stars}</span>
</div>
);
}
render() {
if(this.props.repositories.length > 0 ){
return this.renderRepository();
} else {
return <h4>Loading...</h4>;
}
}
}
Related
Am looking at navigating from App.js(which is the default component) to a new component which is not with in the hierarchy passing a function. Any input how can I do it?
I have a component Question which is used to display questions, also I have Upvote and Downvote component which takes care of upvoting and downvoting respectively.
Here is the code I have in place ..
import React from 'react';
import Question from './Question';
import questions from '../questions';
import './../App.css';
import base from '../base';
import AddQuestionForm from './AddQuestionForm';
import AddQuestionButton from '../AddQuestionButton';
class App extends React.Component {
state = {
questions: {}
};
handleUpvote = key => {
const questions = { ...this.state.questions };
const updateQuestion = questions[key];
updateQuestion.upvotes++;
questions[key] = updateQuestion;
this.setState({ questions });
};
handleDownvote = key => {
const questions = { ...this.state.questions };
const updateQuestion = questions[key];
updateQuestion.downvotes++;
questions[key] = updateQuestion;
this.setState({ questions });
};
addQuestion = question => {
const questions = { ...this.state.questions };
questions[`question${Date.now()}`] = question;
this.setState({ questions });
};
//this is where I want to send the user to /question along with the function addQuestion, since thats the function used to insert a question.
handleAddQuestionNavigation = () => {
// this.props.addQuestion(this.addQuestion);
this.props.history.push(`/question`);
};
componentDidMount() {
this.ref = base.syncState(`questions`, {
context: this,
state: 'questions'
});
// this.setState({ questions: questions })
}
render() {
return (
<div className="App">
{/* <AddQuestionForm
history={this.props.history}
addQuestion={this.addQuestion}
/> */}
{/* <AddQuestionButton
history={this.props.history}
addQuestion={this.addQuestion}
/> */}
<button onClick={this.handleAddQuestionNavigation}>
Add Question
</button>
{Object.keys(this.state.questions).map(key => (
<Question
key={key}
index={key}
details={this.state.questions[key]}
handleUpvote={this.handleUpvote}
handleDownvote={this.handleDownvote}
/>
))}
</div>
);
}
}
export default App;
This is Router.js
import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import App from './App';
import AddQuestionForm from './AddQuestionForm';
import ShowQuestion from './ShowQuestion';
const Router = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={App} />
<Route path="/question" component={AddQuestionForm} />
<Route path="/questions/:qId" component={ShowQuestion} />
</Switch>
</BrowserRouter>
);
export default Router;
This is AddQuestionForm.js
import React from 'react';
class AddQuestionForm extends React.Component {
titleRef = React.createRef();
descRef = React.createRef();
createQuestion = event => {
event.preventDefault();
const q = {
title: this.titleRef.current.value,
desc: this.descRef.current.value,
upvotes: 0,
dedownvotessc: 0
};
this.addQuestion(q);
this.props.history.push(`/questions/question${Date.now()}`);
};
render() {
return (
<form onSubmit={this.createQuestion}>
<input
name="title"
ref={this.titleRef}
type="text"
placeholder="Submit your question"
/>
<textarea
name="desc"
ref={this.descRef}
placeholder="say something about your question"
/>
<button type="submit">Add Question</button>
</form>
);
}
}
export default AddQuestionForm;
Any inputs are much appreciated.
Navigating in react-router-dom should be with the Link/NavLink Components,
to is the path that matches the route
Becuase you want to send a function to /question path.
you could also write Link Component as
Where Link accpets an object containing the pathname, and state which u can access
inside the question component
how can i pass the history to the dynamically created routes which are
wrapped with a another component
see the below snippet
import React, {Component} from 'react';
import {BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Loader from './Loader';
import RootComponent from './RootComponent';
import About from './About';
import Contact from './Contact';
class App extends Component{
state = {
loaderStatus: false,
dynamicRoutes: null
}
componentWillMount(){
this.setState({
loaderStatus: true
})
}
componentDidMount(){
let routes = () => {
let accessedRoutes = [{path:'about', component: () => <About />},{path:'contact', component: () => <Contact />}].map(o => {
return (
<Route
exact={true}
path={o.path}
component={o.component}
>
</Route>
)
})
return accessedRoutes
}
setTimeout(() => {
let output = this.createRoutes(routes)
this.setState({
dynamicRoutes: output,
loaderStatus: false
})
}, 4000)
}
createRoutes = (routes) => {
return (
<RootComponent>
<Switch>
{routes}
</Switch>
</RootComponent>
)
}
render(){
return(
<Fragment>
{
this.state.loaderStatus ?
<Loader />
:
<Router>
{this.state.dynamicRoutes}
</Router>
}
</Fragment>
)
}
}
export default App;
// About.js
import React from 'react';
const about = (props) => {
console.log('props in about', props) // giving empty object
return (
<div>About</div>
)
}
export default about
Change the structure of accessedRoutes to [{path:'about', component:About}].
now you can access to the router props
My application is using React Router(Router.js), I'm trying to implement CentralStore using Context API to pass state to Event.js component rendered by Router.
How to pass context consumer to Component (Event.js) rendered by React Router only? Right now AppContext is undefined in Event.js.
Live code: https://codesandbox.io/s/0x83zz0jmw
const AppContext = React.createContext();
class CentralStore extends React.Component {
state={
events: false,
}
componentDidMount() {
firebase
.collection("events")
.get()
.then( querySnapshot => {
const events = [];
querySnapshot.docs.forEach(doc => {
events.push(doc.data());
});
this.setState({
events: events
});
});
}
render(){
return(
<AppContext.Provider value={{
state: this.state
}}>
{this.props.children}
</AppContext.Provider>
)
}
}
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import App from '../App';
import EventCreator from './EventCreator';
import NotFound from './NotFound';
import Event from './Event';
import Events from './Events';
import CentralStore from '../CentralStore';
const Router = () => (
<CentralStore>
<BrowserRouter>
<Switch>
<Route exact path="/" component={App}/>
<Route path="/create-event" component={EventCreator} />
<Route path="/events/:eventId" component={Event} />
<Route path="/events" component={Events} />
<Route component={NotFound}/>
</Switch>
</BrowserRouter>
</CentralStore>
);
export default Router;
//
const Event = (props) => {
console.log(props);
return (
<div className="event">
<AppContext.Consumer>
{(context) => (
console.log(context)
)}
</AppContext.Consumer> */}
</div>
);
}
export default Event;
Problem was solved by exporting AppContext from CentralStore component
// The missing export
export const AppContext = React.createContext();
class CentralStore extends React.Component {
state={
events: false,
}
componentDidMount() {
firebase
.collection("events")
.get()
.then( querySnapshot => {
const events = [];
querySnapshot.docs.forEach(doc => {
events.push(doc.data());
});
this.setState({
events: events
});
});
}
render(){
return(
<AppContext.Provider value={{
state: this.state
}}>
{this.props.children}
</AppContext.Provider>
)
}
}
I'm having issues accessing a parameter called bookId from the Reader.js component. The parameter is passed down from BookCarouselItem.js using react-router. Reader.js is a connected component.
I'm not sure if that makes a difference, but does react-router work with redux connected components? Or do I need to use something like connected-react-router?
I've tried to refer to similar questions but wasn't able to find a solution, help would be greatly appreciated.
App.js
import React, { Component } from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import { routes } from 'constants/index';
import Reader from 'components/reader/Reader'
Class App extends Component {
render() {
return (
<div className='container-fluid main-container'>
<Router>
<div>
<Route
path={'/reader/:bookId'}
component={() => <Reader />}
/>
</div>
</Router>
</div>
);
}
}
export default App;
BookCarouselItem.js
import React from 'react'
import { Link } from 'react-router-dom'
export class BookCarouselItem extends React.Component {
render() {
const { bookThumbnail } = this.props;
const { name, numberOfSections } = bookThumbnail;
const bookId = 0;
return (
<Link className='book-carousel-link' to={`/reader/${bookId}`}>
<div className='book-info-overlay'>
<h5>{name}</h5>
<span>{numberOfSections} Sections</span>
</div>
</Link>
);
}
}
export default BookCarouselItem;
Reader.js
import React from 'react'
import { connect } from 'react-redux'
import { compose } from 'recompose'
export class Reader extends React.Component {
render() {
const { match, pageLevel } = this.props;
console.log(match); // undefined
return (
<div>
<div className='reader-body'>
<Book bookId={match.params.bookId}
pageLevel={pageLevel}
bank={bank}/>
</div>
);
}
}
Const mapStateToProps = (state) => {
return {
metadata: state.book.metadata,
pageLevel: state.book.pageLevel
}
};
const authCondition = (authUser) => !!authUser;
export default compose(
withAuthorization(authCondition),
connect(mapStateToProps, mapDispatchToProps),
)(Reader);
You can just give the component to the component prop and the route props will be passed down to the component automatically.
<Route
path="/reader/:bookId"
component={Reader}
/>
If you want to render something that is not just a component, you have to pass down the route props manually.
<Route
path="/reader/:bookId"
render={props => <Reader {...props} />}
/>
I'm not sure but maybe mapStateToProps rewrite you props so could you please first read this issue
I have a static component called Item.js
Routes.js
export default () => (
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route path="/posts" component={Posts} />
<Route path="/form" component={Postform} />
<Route exact path="/" component={App} />
<Route path="/items" component={Items} />
<Route path="/cart" component={Cart} />
<Route path="/page/:id" component={Page} />
</Switch>
</BrowserRouter>
</Provider>
);
In the above page component, I want to load item.js or any other page depending on whats passed to the URL params in as "id" in the page component.
import React, { Component } from 'react';
import Navbar from './Navbar';
import Item from './pages/Item';
class Page extends Component {
componentDidMount() {
const { id } = this.props.match.params;
console.log(id);
}
render() {
return (
<div>
<Navbar />
<div>Hello</div>
</div>
);
}
}
export default Page;
How do I achieve this? I don't know.
Are there any alternative ways of doing it?
Ok, I solved it by following a variation of Johnny Peter's answer.
Page.js
import React, { Component } from 'react'
import Navbar from './Navbar';
import components from './indexPage'
class Page extends Component {
render() {
const { id } = this.props.match.params;
const PageComponent = components.find(comp => comp.id === id).component;
return (
<div>
<Navbar />
<PageComponent/>
</div>
);
}
}
export default Page;
indexPage.js
import Item from './pages/Item'
import Meow from './pages/Meow'
const components = [{
id: 'item',
component: Item
},
{
id: 'meow',
component: Meow
}
]
export default components;
Something like this should work
import React from 'react';
import Navbar from './Navbar';
import Item from './pages/Item';
const components = [{
id: 'your/id/passed/in/param'
component: Item
}]
class Page extends React.Component {
state = {
Component: null,
}
componentDidMount() {
const { id } = this.props.match.params;
this.setState({ Component: components.find(comp => comp.id === id).component })
}
render() {
const { Component } = this.state;
return (
<div>
<Navbar />
<Component />
</div>
);
}
}
export default Page;
You can create an index file like so:
index.js:
import Item from './item';
const pages = {
pageId: Item,
};
export default pages;
And in page component:
import React, { Component } from 'react';
import Navbar from './Navbar';
import Item from './pages/Item';
import pages from './pages';
class Page extends Component {
componentDidMount() {
const { id } = this.props.match.params;
console.log(id);
}
render() {
const { id } = this.props.match.params;
if (pages[id]) {
return pages[id];
}
return (
<div>
<Navbar />
<div>Hello</div>
</div>
);
}
}
export default Page;
in order to dynamically load component based on id.