How to get local state of another component using Redux - javascript

Using react + redux, I am making an app where you answer questions, 5 questions per step and about 20 steps. When starting a new step, I save the progress to localStorage.
My app structure is like this:
class App extends Component {
render() {
return (
<Provider store={store}>
<Router>
<div id="ocean-model">
<Topbar />
<main id="main">
<Route exact path="/" component={Landing} />
<Switch>
<PrivateRoute exact path="/survey" component={Survey} />
</Switch>
<Switch>
<AdminRoute exact path="/edit-questions" component={EditQuestions} />
<AdminRoute exact path="/add-question" component={AddQuestion} />
</Switch>
</main>
</div>
</Router>
</Provider>
);
}
}
In Survey component I use controlled inputs to save current answers to local state and on submitting all 5 answers I use redux action to save given answers to localStorage and redux store.
In Topbar component I have a button, which onClick I would like to take the current progress from redux store, but also get the currently answered questions(for example 2 out of 5), which are only available in the Survey component's local state.
Do I need to modify the App structure so that Topbar and Survey can share state or maybe in Survey I need to somehow listen to an onclick event of a button from Topbar?

Is there any reason you can't save given answer to localStorage and redux store when the input loses focus? Might need to allow for the action to fire if the Topbar component button is clicked while the focus is still on the last input with an answer.
The alternative would be to use React Context as your state container for each set of 5 questions and use that from both the form and the Topbar button with a Provider located in the component tree above both.

Related

How to implement data update from one component which might affect other components

I am implementing a todo application which has several pages where each page displays a filtered list of all todos depending on the todos' references.
In addition, there is a global "Add task" button and modal from which users can add a task with references. Depending on which page the user is on when adding a task, this newly added task must either be shown right away because the reference is included in currently active page's filter or it must not be shown.
For example, if the user is on the page "Project 1"
If user adds task for Project 1 -> show on page
If user adds task for Project 2 -> don't show on page
Currently, App.js looks something like this
function App() {
return (
<Page>
<Router>
<Route path="/project1" element={<TaskListProject1 />} />
<Route path="/project2" element={<TaskListProject2 />} />
<Router />
<AddTaskButtonAndModal />
<Page />
);
}
How do I best set up and manage the state so that the components TaskListProject1 and TaskListProject2 are only updated if the added task must be included there?
I was thinking about adding the task from the AddTaskButtonAndModal component to the data base and then let the currently active component retrieve all their tasks from the data base again. For this, I would introduce a dummy state whose sole purpose is triggering a rerender of components like so
function App() {
const [dummyState, setDummyState] = useState(0);
return (
<Page>
<Router>
<Route path="/project1" element={<TaskListProject1 dummyState={dummyState} />} />
<Route path="/project2" element={<TaskListProject2 dummyState={dummyState} />} />
<Router />
<AddTaskButtonAndModal setDummyState={setDummyState} />
<Page />
);
}
...but that feels like I'm misusing states.
Another idea was to always have a complete list of all tasks in the top level state and let each component filter for their tasks when it is rendered. But then I need to always have all tasks in the state when only a fraction of tasks are actually needed.
As you can probably imagine, I'm new to React, so I might simply miss a basic concept. Thanks for any help!

nav component losing props. React router mistake?

First time using react router and I am losing props passed to my nav component once I render a new route. Maybe losing props is wrong way of explaining it. What problem is on home page when i add items to cart a badge on the nav updates with correct number of items but when i click checkout they disappear. When I click back to home the badge displays correctly again.
I think i must be something simple because the app worked before I added router.
1. Home works
2. No cart items shown:
Notes
— NavMain component not added to a Route because I wanted it displayed on both checkout page and home page
— between the render() and the return code counts amount of items in cart and passes the number to the nav through props note added to state.
I think i did something wrong in App sensing that <.NavMain ... /> needs be in a route?
class App Extends React.Component {
render() {
/* counts number of items in cart does not add to state */
const arrayOfQuantities = this.state.cart.map(item => item.quantity);
const countOfCartItems = arrayOfQuantities.reduce(
(total, current) => (total += current)
);
return (
<BrowserRouter>
<div className="App">
/** Is this the problem? **/
<NavMain
countOfCartItems={countOfCartItems}
onTermSubmit={this.onTermSubmit}
handleSearched={this.handleSearched}
itemsInCartBoolean={this.state.cart.length > 1}
/>
<Route
exact
path="/"
render={() => (
<React.Fragment>
<MainCarousel />
<WatchList
cart={this.state.cart}
addCartItem={this.handleAddCartItem}
watchList={data.products[0].frankMuller}
/>
</React.Fragment>
)}
/>
<Route
path="/checkout"
render={() => <Checkout cart={this.state.cart} />} // used for pricing etc.
/>
</div>
</BrowserRouter>
);
}}
export default App;
Checkout component below, I think i'm meant to pass props through to that right? thats not a bootstrap component thats my component which i imported for the checkout I will try pass some props omg fingers crossed. Will try click some buttons.
export default class Checkout extends Component {render() {
return (
<div>
<NavMain />
<h1 className="main-header">Checkout</h1>
<h5 className="sub-heading">YOUR ORDER</h5>
<OrderList
cart={this.props.cart}
addCartItem={this.props.addCartItem}
removeCartItem={this.props.removeCartItem}
/>
);}}
Syed knew where to look the problem was the Checkout component I added the NavMain component in there when it was already added inside the root App. I simply deleted it from the Checkout and left root alone.
works now ^^ thanks

React Router Link dont updated page data

I have this react + redux app
I have 1 Movie component with some movie data displayed on it which are fetched from API.
So at the bottom, i decided to add a Similar movies section with a couple of components to navigate to the new movie pages.
<div className="similar_movieS_container">
{ this.props.thisMovieIdDataSIMILAR.slice(0,4).map((movie,index)=>{
return(
<Link to={"/movie/"+movie.id} key={index}>
<div className="similar_movie_container">
<div className="similar_movie_img_holder">
<img src={"http://image.tmdb.org/t/p/w185/"+movie.poster_path} className="similar_movie_img" alt=""/>
</div>
</div>
</Link>
)
})
}
</div>
Now when I'm at a root route for instance /toprated and i click on a <Link to={"/movie/"+movie.id} key={index}> i get navigated to the particular route (e.g. /movie/234525 ) and everything works fine , but if I'm in a /movies/{some move ID } route and i click on some <Link to={"/movie/"+movie.id} key={index}> The route in the URL bar gets updated, but the page stays still, meaning nothing changes if I reload the page with the new route the new movieID data is displayed...
So how can i make a navigation to a new /movie/{movieID} FOMR a /movie/{movieID} ?
It's because you are just within same route.
There are few ways to work around this. I bet you are rendering routes like this.
<Route path="/movie/:id" component={Movie} />
Try like this.
<Route path="/movie/:id" component={() => <Movie />} />
Another solution would be to use componentWillReceiveProps instead of componentWillMount.
This happens because the component of page movie was mounted, so the render method of this component will not be mounted again or updated, so you need to fire a code to change the state, and consequently re-rendering the page content, for this you can implement the componentDidUpdate method which is fired for every props modification.
https://reactjs.org/docs/react-component.html#componentdidupdate
Basically you implement the same code above inside

Basic way to handle login with React

I'm working on a client-side app using create-react-app. The application renders a login component with a basic form and I want to load another component (which will be the main application) on successful login.
The validation and the login logic is not the issue at the moment because first I'm trying to figure out a simple way to dismount the login component and load another component on the submit event.
class Login extends Component {
handleLogin(){
// trigger to load Main.js
}
render() {
return (
// form elements here
<div className="submit">
<input className="button-signin" value="Sign In" type="submit"
onClick={this.handleLogin}/>
</div>
);
}
}
What would be the easiest way to switch to another component (which I called Main.js in this example) on submit event?
Have you tried using React router? It makes really easy to define routes and assign a Component to each of them. Then you can also establish conditions for accessing each route. There is an example in the official docs that seems to match your requirements.
<Router history={withExampleBasename(browserHistory, __dirname)}>
<Route path="/" component={App}>
<Route path="login" component={Login} />
<Route path="logout" component={Logout} />
<Route path="about" component={About} />
<Route path="dashboard" component={Dashboard} onEnter={requireAuth} />
</Route>
The method requireAuth will be checked each time you try to access the route dashboard (which will show the component Dashboard). If you are using token-based authentication, you might have a token stored in the localStorage and check if it's present in requireAuth.
Your method handleLogin would call the backend, log the user in (store the token in localStorage if that's how the login is handled) and then redirect to the dashboard route.

Unable to access function from parent - sending props from parent to child

I have an app which has a navbar, and a display below that shows all the posts.
Here is how it is setup:
var routes = (
<Router history={createHistory()}>
<Route path="/" component={App}>
<Route path="/submit" component={CreatePost}/>
</Route>
<Route path="*" component={NotExist}/>
</Router>
)
So, if you click on "New" in the navbar, you will be taken to server/submit and a form will appear where you can enter details for a new post. On submit, it will add the details to the posts state object.
Inside of App, I have a function called addToPosts().
render : function() {
//<CreatePost addPostToPosts={this.addPostToPosts} posts={this.state.posts}/>
return (
<div>
<NavigationBar/>
{this.props.children}
<DisplayPosts postData={this.state.posts} />
</div>
)
}
#kirill-fuchs yesterday told me to use {this.props.children} to send the properties. However, when I check react console, I see that props are empty. But, I know it is doing something because if I get rid of the {this.props.children} then clicking on New doesn't do anything. But when I put it back, it redirects to the form.
In addition, when I click submit in the form, it says this.props.addToPosts is not a function, because the props are empty.
Can someone please help me?
It sounds like you want to pass props down to the children. You can use the following to do that:
{React.cloneElement(this.props.children, {addToPosts: this.addPostToPosts, posts: this.state.posts})}
This will pass a prop into the CreatePost component that you can then call when the submit button is pressed.

Categories

Resources