React Router Switch Redux Dispatch - react-redux

I have the below switch statement that routes the user to correct component based on the link they are on.
const Router = (props) => {
switch(props.page) {
case 'Equities' :
this.props.pageName('Equities');
this.props.pageURL('/Equities');
return <Equities />;
case 'Daily' :
return <Daily />;
default :
return ( <Redirect to="/Equities" /> )
}
}
const content = ({ match }) => {
return (
<div className="content">
<Router page={match.params.type} />
</div>
);
}
const mapDispatchToProps = {
pageURL,
pageName
};
export default connect(mapDispatchToProps)(content);
On the 4th line above, I am trying to dispatch an action to Redux to update page name and URL in the redux store that the user is on. I get the below error:
How can I dispatch actions based on the page user is on so I update name and URL to whichever page user is visiting?

So, for anyone experiencing this problem, it seems to be caused by my error on adding redux to the page crashing with the compose module.
My component structure for the app is like this:
App -> Skeleton -> TopBar, Sidebar, Content
So inside Content component I have a switch that displays the correct content for user. I was trying to add this functionality to Content. Now I added to Skeleton, and it works fine and is much better because I don't need to add now 8 different statements inside switch saying if match this do this do that. Now all I have is this.props.pageName(match.url); this.props.pageURL(match.params.type); so I record in redux the user is on and that's all.

Related

Remix.run - common shared components

I’m just getting started learning remix.run and whilst I’ve gone through the tutorials there’s one bit I’m stuck on how I should implement it in remix.
If I wanted to display a common header that might toggle a sign in/sign out button based on the users logged in state where would this live?
My nextjs thinking would be to create the components and reference them in the common document. I know I can do this in the remix.server and remix.client files, but as my “login” component is and isn’t a route (I.e I might want to POST to the route when a user submits the login form but GET /login isn’t really a route) how would you structure something like this and would doing this even allow me to have loader and action functions in the shared component?
Do I just need to adjust my thinking about how to achieve this in remix or am I overthinking it and the above is perfectly valid?
I tried the following and it works. But then I end up just creating an empty "logout" route to process the form data with an action and loader that process the form in the case of the action or just redirect if a GET request via the loader. Is this the best approach?
export const SignIn = ({user}) => {
return (
<>
<form method="POST"action="/logout">
<input type="hidden" id="some" value="foo" />
{user ?
(
<button>sign out</button>
)
: (
<button>sign in</button>
)
}
</form>
</>
)
}
Thanks
based on https://remix.run/docs/en/v1/tutorials/jokes#build-the-login-form
it does indeed look like an empty route for logout:
import type { ActionFunction, LoaderFunction } from "#remix-run/node"
import { redirect } from "#remix-run/node"
import { logout } from "~/utils/session.server"
export const action: ActionFunction = async ({request}) => {
return logout(request);
};
export const loader: LoaderFunction = async () => {
return redirect("/");
};

redux-form only show validation errors on submit

Is there a way to configure redux-form so that validation errors only show when the form is submitted?
Also I would like to clear an individual form field error when a user starts typing or makes a change.
Is this possible with redux-form?
You're responsible for rendering any validation errors, so you can configure them only to be visible after submission has failed (i.e. the user pressed submit and your validations came back as failing). For this, your input components wrapped with Field are passed a meta object as a prop that contains a submitFailed field that tells whether form submission has failed. The actual code for rendering that component could look something like this:
render() {
const { meta: { error, submitFailed } } = this.props
return (
// ... rendering the input and then some
{ error && submitFailed ? (
<span className="validation-error">{ error }</span>
) : null }
// ... rendering the rest
)
}
For not showing errors while the user is typing, you're responsible for enabling that behavior and the props passed to your wrapped input component hold the keys, this time the meta object holds a field called active, which tells you whether this input has focus or not. Code could be something like this (building on the previous example):
render() {
const { meta: { active, error, submitFailed } } = this.props
return (
// ... rendering the input and then some
{ !active && (error && submitFailed) ? (
<span className="validation-error">{ error }</span>
) : null }
// ... rendering the rest
)
}
Hope this helps!
I'm also had this problem, the reason was using touchOnChange flag
reduxForm({
form: 'MyForm',
enableReinitialize: true,
touchOnChange: true,
})
So I removed this flag from reduxForm options and the code started working correctly - I saw the validation errors on submit only
reduxForm({
form: 'MyForm',
enableReinitialize: true,
})
Example from official site
https://redux-form.com/8.2.2/examples/fieldlevelvalidation/

Avoid double ajax call

I'm really new to React so I'm trying to manage it by made some examples. I made this component that made an ajax call to render a simple list.
import React from "react";
import axios from 'axios';
import Page from '../components/Page/Page';
import ListJobs from '../components/ListJobs/ListJobs';
let state ;
class Home extends React.Component{
constructor(props){
super(props);
this.state ={jobs:[]};
}
componentDidMount(){
var _this = this;
this.serverRequest = axios.get("http://codepen.io/jobs.json")
.then(function(result){
_this.setState({
jobs:result.data.jobs
});
});
}
componentWillUnmount() {
}
render(){
return(
<div>
<Page title="Home">
<p>Home</p>
<ul>
{this.state.jobs.map(function(job,index) {
return(
<ListJobs key={index} job={job}/>
);
})}
</ul>
</Page>
</div>
);
}
}
export default Home;
It calls another child component to render the li elements.
Everytime I call this component it starts with this ajax call, so I was wondering if there is a way to save the result of this ajax call and re-use it, instead of launching every time the request. I tried to do like this https://jsfiddle.net/corvallo/mkp4w8vp/
But I receive this error in the developer console:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of Home
Thank you in advance for your help
If you want the ajax calls only when the app launches, then you can make ajax calls on the parent component (probably App) and then pass it as a props to the Home component
EDIT
if you want to call the ajax only from the component, I think you can implement a cache or some sort e.g using localstorage
example
componentDidMount(){
let cache = JSON.parse(localStorage.getItem('homeCache') || "{}");
if(cache.hasOwnProperty('cached') && cache.hasOwnProperty('jobs')){
this.setState({jobs:cache.jobs});
}else{
(/*YOUR API HERE*/).then(function(result){
_this.setState({jobs:result.data.jobs});
localStorage.setItem('homeCache', JSON.stringify({cached: true, jobs: result.data.jobs}));
});
}
}
and everytime when the user exits the app, clear the cache (or anytime you want the cache to be cleared)
localStorage.setItem('homeCache', JSON.stringify({}));
I think that's one solution which popped out of my head right now..

Redirect from method in Vue.js with Vue-router older than version 1.x.x

I'm not much of a frontend developer but I know enough javascript to do the minimum.
I'm trying to plug into a last piece of login however my vue components are:
"vue-resource": "^0.9.3",
"vue-router": "^0.7.13"
I'm not experienced enough to move up to v1 or v2 respectively.
I would like to achieve something similar to this.
However I'm not getting a successful redirect.
my app.js file:
var router = new VueRouter();
...
import Auth from './services/auth.js';
router.beforeEach(transition => {
if(transition.to.auth &&!Auth.authenticated)
{
transition.redirect('/login');
}
else
{
transition.next();
}
});
```
In my login.js file
```
methods: {
/**
* Login the user
*/
login(e) {
e.preventDefault();
this.form.startProcessing();
var vm = this;
this.$http.post('/api/authenticate',
{ email : this.form.email,
password : this.form.password
})
.then(function(response){
vm.form.finishProcessing();
localStorage.setItem('token', response.data.token);
vm.$dispatch('authenticateUser');
},
function(response) {
if(response.status == 401)
{
let error = {'password': ['Email/Password do not match']};
vm.form.setErrors(error);
}else{
vm.form.setErrors(response.data);
}
});
}
}
I tried to do as suggested:
vm.form.finishProcessing();
localStorage.setItem('token', response.data.token);
vm.$dispatch('authenticateUser');
vm.$route.router.go('/dashboard');
However all it did was append the url on top.
I see that the 3 previous events were successfully done but not the redirect.
it went from:
http://dev.homestead.app:8000/login#!/
to
http://dev.homestead.app:8000/login#!/dashboard
when I need the entire page to go to:
http://dev.homestead.app:8000/login/dashboard#1/
I think i have a missing concept in order to do the redirect correctly.
UPDATE
As per suggested i have added param: append => false but nothing happens.
what i did afterward was within app.js create a method called redirectLogin() with console.log() outputs - that worked. what i did further is i put vm.$route.router.go inside there and called the method via vm.$dispatch('redirectLogin'); and that also didn't work.
NOTE:
The HTML is being rendered in Laravel first. the route I originally had (and working) as login/dashboard and that route is available via normal Laravel route. the blade is being rendered via view template.
So far I've been trying to vue redirect over to login/dashboard (not working) perhaps I should somehow remove login/dashboard and use the route.map and assign login/dashboard?
I would rather keep the login/dashboard as a laravel route due to authentication and other manipulation.
For Vue 2
this.$router.push('/path')
As par the documentation, router.go appends the path in the current route, however in your case it is appending along with # in the router as well.
You can use param: append, to directly arrive at your desired destination, like following:
vm.$route.router.go({name: '/login/dashboard#1/', params: {append: false}})
Edited
If it is not happening, you can try $window.location method like following:
var url = "http://" + $window.location.host + "login/dashboard";
console..log(url);
$window.location.href = url;
I think their is a misunderstanding here of how vue-router works. It seems you are not wanting to load a new route with a corresponding component, rather you simply want to redirect to a new page then let that page load and in turn fire up a fresh instance of vue.
If the above is correct you don't need vue-router at all. Simply add the below when you need to load the page:
window.location.href = '/login/dashboard'
If you'd rather simulate a redirect to that page (no back button history) then:
window.location.replace('/login/dashboard')
EDIT
The above would be fired when you have finished all processing that the page must run to set the users state which the next page requires. This way the next page can grab it and should be able to tell the correct state of the user (logged in).
Therefore you'll want to fire the redirect when the Promise has resolved:
.then(function(response){
vm.form.finishProcessing()
// store the Auth token
localStorage.setItem('token', response.data.token)
// not sure whether this is required as this page, and in turn this instance of vue, is about to be redirected
vm.$dispatch('authenticateUser')
// redirect the user to their dashboard where I assume you'd run this.$dispatch('authenticateUser') again to get their state
window.location.replace('/login/dashboard')

Redux Form - Not able to type anything in input

Hi I have upgraded to redux-form version 6.0.0 recently. And I am facing an issue like I am not able to type anything in the text field.
P.S I am also using Reactintl. I am using compose to aggregate connect, reduxform and intl decorator
Here is my code
Pastebin
If I understand correctly, then starting with v6 you should provide extra onBlur and onChange methods for the input in order to update its state. For your stateless component renderInput it could be done like this:
const renderInput = (field) => {
const onBlur = () => {
field.input.onBlur(field.input.value);
};
const onChange = (inputValue) => {
field.input.onChange(inputValue ? inputValue : '')
};
return <input {...field.input} onBlur={onBlur} onChange={onChange}
[...other options omitted for readability] />
}

Resources