React Navigation 6 attempting to pass different params to same screen - react-navigation

I currently have the following setup an onboarding stack component:
export default function OnboardingStack(props) {
const { auth, activeUser } = useContext(FirebaseContext);
return (
<Stack.Navigator mode="card" headerMode="none">
<Stack.Screen
name="Login"
component={Login}
option={{
headerTransparent: true
}}
/>
<Stack.Screen name="App" component={AppStack} />
</Stack.Navigator>
);
}
Then I can call a component called MemberList which contains a touchable opacity:
<TouchableOpacity style={styles.touchableRow} onPress={ () => navigateMember(item) }>
the method navigateMember I navigate to "Add Member"
const navigateMember = (item) => {
navigation.navigate("Add Member", {
screen: "Member",
params: {
uid: item,
}
}
);
}
Here item is different each time I click it but when I get into the "Member" screen it retains the first original passed uid. Member component contains:
useEffect(() => {
navigation.addListener('focus', () => {
// console.log(navigation);
console.log('this member route');
console.log(route);
})
navigation.addListener('blur', () => {
console.log('leaving blur');
navigation.setParams({
key: route.key,
params: { uid: 'og' },
})
})
}, [])
Each time the uid remains the same. I've tried to reset it when it blurs but it always retains the same params. Any idea what I'm doing wrong?

The solution was to use push as opposed to navigate.
onPress={() => navigation.push('Member')}
More details found in the documentation

Related

formik initial checkbox value not disabling submit button

In my form, I have a checkbox for captcha that needs toggled for the form to be valid, however on load, the form still shows as valid. I can check the box to enable the form, and then uncheck it again, and THEN the form shows as properly disabled.
I am using the HOC withFormik implementation. I have tried using setTouched & setFieldTouched on the checkbox, and even setting this.props.touched.captcha = true. I have also tried passing isInitialValid: false to withFormik.
Another thing I tried is passing mapPropsToTouched, but that method doesn't get called.
I am using formik v 1.5.8
How can I make the form initially disabled until I engage the checkbox?
export class LoginForm extends React.Component {
render() {
const {
initialValues,
initialTouched,
validationSchema,
onSubmit,
children
} = this.props;
return (
<div>
<Formik
initialValues={initialValues}
initialTouched={initialTouched}
validationSchema={validationSchema}
onSubmit={onSubmit}
render={(formikProps) => {
return (
<Form>
{React.Children.map(children, (child, index) => {
return (
<div className='Form__row'>
{React.cloneElement(child, { index })}
</div>
);
})}
</Form>
);
}}
/>
</div>
);
}
}
const mapDispatchToProps = {};
export function mapStateToProps(state) {
return {};
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withFormik({
// isInitialValid: false,
// mapPropsToTouched: (props) => {
// console.log('馃殌 ~ file: LoginForm.jsx ~ line 257 ~ props', props);
// },
mapPropsToValues: (props) => {
return {
username: props.previousUsername ? props.previousUsername : '',
password: '',
rememberMe: true,
captcha: false
};
}
})
)(LoginForm);

React-Redux: send a fetched API data from a component to a dynamic route component using a button inside a table

I'm building an app with react-redux and have 3 main problems. In a volenteer component, I get data of volenteers from the store and then show it in a table component by passing it the data and columns (imported as constants).
The Volenteer component :
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { requestVolunteerData } from '../actions';
import { volenteerColumns as columns } from '../utils/constants';
import '../container/App.css';
import Table from '../components/Table/Table';
import Loading from '../components/Loading/Loading';
import {Link} from 'react-router-dom';
const mapStateToProps = state => {
return {
entities: state.requestEntitiesReducer.entities,
isPending: state.requestEntitiesReducer.isPending,
error: state.requestEntitiesReducer.error
}
}
const mapDispatchToProps = dispatch => {
return {
onRequestEntities: () => dispatch(requestVolunteerData())
}
}
class Volenteer extends Component{
componentDidMount () {
this.props.onRequestEntities();
}
render () {
const { entities, isPending} = this.props;
return isPending ?
<Loading />
:
(
<div className='tc'>
<h1 className='f1'>专砖讬诪转 诪转谞讚讘讬诐</h1>
<Table data={ entities } columns={ columns }/>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Volenteer);
The constants file - contain the volenteerColumns :
import {Link} from 'react-router-dom';
// Types
export const REQUEST_ENTITIES_PENDING = 'REQUEST_ENTITIES_PENDING';
export const REQUEST_ENTITIES_SUCCES = 'REQUEST_ENTITIES_SUCCES';
export const REQUEST_ENTITIES_FAILED = 'REQUEST_ENTITIES_FAILED';
// Columns
export const volenteerColumns = [{
dataField: 'name',
text: '砖诐 驻专讟讬'
}, {
dataField: 'name',
text: '砖诐 诪砖驻讞讛'
}, {
dataField: 'phone',
text: '注讬专'
},{
dataField: 'yeshuv',
text: '讻转讜讘转'
},{
dataField: 'phone',
text: '诪住驻专 讟诇驻讜谉'
},{
dataField: 'area',
text: '讗讬讝讜专 驻注讬诇讜转 诪讜注讚祝'
},{
dataField: 'occupation',
text: '转讞讜诐 注讬住讜拽'
}, {
dataField: 'alertDonationInArea',
text: '讛转专讗讛 注诇 转专讜诪讛 讘讗讬讝讜专'
}, {
dataField: 'alertdonationNearBy',
text: '讛转专讗讛 注诇 转专讜诪讛 拽专讜讘讛'
}, {
dataField: 'status',
text: '住讟讟讜住'
}, {
dataField: 'bDate',
text: '转讗专讬讱 讬讜诐 讛讜诇讚转'
}, {
dataField: "_id",
text: "驻注讜诇讜转",
formatter: (rowContent, row) => {
return (
<Link to={`/volenteerRoute/${_id}`}>
<button className='btn btn-outline-primary btn lg'>view</button>
</Link>
)
}
}];
and its look like this:
all I want is that when I click on the view button, it will go to a new route: <Route path='/volenteerRoute/:id' component={VolenteerDetails} />
and will show te data of this specific volenteer.
my problems are:
For now I want to import the volenteer columns from external file if it possible (for readability reasons and short code) which is not realy constants... because the button should point to a dynamic route,
so how do I change my route by clicking the view button?
I know I don't write it correctly (how to use the formatter)- what is the right way to pass te volenteer ID by that button that found in an external file?
What is the correct way for passing the state of the volenteer to the <Route path='/volenteerRoute/:id' component={VolenteerDetails} />, is it by a Link like this:
<Link to ={{pathname: "/volenteerRoute/:id",
state: { volenteerDetails:
this.state.volenteerDetails}}} >
or by an action of redux (preferred) by another fetch API call? if so, how do I fetch this data? those are my action:
//volunteer
export const requestVolunteerData = () => getData('http://localhost:8000/api/volunteer');
export const requestOneVolunteerData = () => getData('http://localhost:8000/api/volunteer/:id');
that call the getData function:
import {
REQUEST_ENTITIES_PENDING,
REQUEST_ENTITIES_SUCCES,
REQUEST_ENTITIES_FAILED
} from './constants';
export const getData = (url) => (dispatch) => {
dispatch ( {type: REQUEST_ENTITIES_PENDING} );
fetch(url)
.then ( response => response.json() )
.then( resp => dispatch({type: REQUEST_ENTITIES_SUCCES, payload: resp.data }) )
.catch(error => dispatch({type: REQUEST_ENTITIES_FAILED, payload: error}) )
}
but of course the second action does not work because I dont know how to pass the ID from the view button to that action
How does the VolenteerDetails (component that should show a specific volenteer details) get the data? is it by props?
const VolenteerDetails = ({ props}) => {
console.log(props);
return (
<div>
<h2>volenteer details </h2>
</div>
)
}
sorry for the length of this question and thanks for any help!!
Add this Inside route.js:
<Route exact path='/volenteerRoute' component={Volenteer} />
<Route path='/volenteerRoute/:id' component={VolenteerDetails} />
Table view button:
<Link to={{pathname: `/volenteerRoute/${item.id}`}}>View</Link>
You can get the id of clicked view using react-router useParams().
VolenteerDetails.js:
const VolenteerDetails = ({ props }) => {
let { id } = useParams();
console.log(id);
// with id you can fetch API or call action even read data from store
...
}
For making action for VolenteerDetails:
export const requestOneVolunteerData = (id) => getData(`http://localhost:8000/api/volunteer/${id}`);
Bonus: For the best experience using redux and routing I prefer using connected-react-router
Thanks! it was very helpful !! except from the Table view button, it should be:
{
dataField: "_id",
text: "驻注讜诇讜转",
formatter: (cell) => {
return (
<Link to={`/volenteerRoute/${cell}`}>
<button className='btn btn-outline-primary btn lg'>view</button>
</Link>
)
}
}
because the cell of the dataField: "_id" is the one that holds the id of the volenteer here is a helpful link:
https://allenfang.github.io/react-bootstrap-table/advance.html
but I could not make it without your answer, so thanks again!!
I will explore connected-react-router as you recommended

passing component state to redux-form onSubmit

novice user of redux-form here. I have a signin modal that has 2 different operations: login and register. The role (stored in component state) will be login by default, and the user will be able to click a button to change it to register.
Where I'm stuck, is that I want to pass that piece of state to the onSubmit() function, so that I can dispatch the correct actions depending on if the user is trying to login or register.
My thinking was that I could pass down this piece of state called signInType as a prop to the function. Of course, it is not working as I would have expected. I can pass in a prop via the reduxForm HOC, but from that function I cannot access the component's state.
Here are the relevant parts of my component to help understand what my end goal is here:
const [signInType, setSignInType] = useState('login')
const onSubmit = (data, dispatch, props) => {
console.log('props: ', props);
if (props.signInType === 'login') {
return (
api.post('/Login', data)
.then(json => {
const response = JSON.parse(json.d)
if (!response.userid) {
console.error(response.message)
dispatch(emailLoginFailure(response.message))
return response.message
}
LogRocket.identify(response.userid, {
email: data.email,
})
dispatch(emailLoginSuccess(response))
})
.catch(err => {
console.error(err)
dispatch(emailLoginFailure(err))
})
)
} else if (props.signInType === 'register') {
return (
api.post('/RegisterByEmail', {
email: data.email,
password: data.password,
utm_source: "Development",
utm_medium: "email",
utm_campaign: "Campaign Test",
utm_term: "N/A",
utm_content: "123",
utm_date: "2019-02-11 12:25:36"
})
.then(json => {
const response = JSON.parse(json.d)
if (!response.userid) {
console.error(response.message)
dispatch(emailRegisterFailure(response.message))
return response.message
}
// LogRocket.identify(response.userid, {
// email: data.email,
// })
dispatch(emailRegisterSuccess(response))
})
.catch(err => {
console.error("Unable to register email:", err)
})
)
} else {
console.error("error: No signin type?")
}
}
Thanks for the help :)
Such login/register flow I prefer handling it with different components, in order to respect and follow SRP.
Also, I'm not sure how do you organize your components, but here's how I deal with such a scenario:
Your Modal:
* It will be responsible only for rendering Login or Register forms.
const Modal = () => {
const [signInType, ] = useState('login')
const isLogin = signInType === 'login'
return <>
{ isLogin ? <LoginForm /> : <RegisterForm /> }
<button onClick={() => setSignInType(isLogin ? 'register' : 'login')}>
{ isLogin ? 'Sign up' : 'Sign in' }
</button>
</>
}
LoginForm:
* Now you can pass your login action to onSubmit prop. Login will be your presentation component, while LoginForm decorates Login with reduxForm HOC.
export default reduxForm({
form: 'login',
onSubmit: data => {}
})(Login)
RegisterForm:
* Here we follow the same idea as LoginForm.
export default reduxForm({
form: 'register',
onSubmit: data => {}
})(Register)

Redux: How to pass store to form created outside the Provider scope

I have written code, which uses a Modal dialog to display a form.
My react app is rendered at "root"
index.html
<div id="root"></div>
App.js
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<ExampleBasic/>
</Provider>
, document.getElementById('root'));
ExmpleBasic.js
Please ignore state management in component here. this is just for example.
import React, { PureComponent } from 'react';
import Lorem from 'react-lorem-component';
import Modal from '#atlaskit/modal-dialog';
import Button from '#atlaskit/button';
export default class ExampleBasic extends PureComponent {
state = { isOpen: false }
open = () => this.setState({ isOpen: true })
close = () => this.setState({ isOpen: false })
secondaryAction = ({ target }) => console.log(target.innerText)
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<BasicFormContainer />
</Modal>
)}
</div>
);
}
}
BasicFormContainer.js
const mapStateToProps = state => ({
addDesignation: state.designations.addDesignation,
});
const mapDispatchToProps = dispatch => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicForm);
BasicForm.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
class BasicForm extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit(values) {
console.log(values);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.submit)}>
<Field
name="designationName"
component="input"
placeholder="Name"
label="Enter name"
autoFocus
/>
</form>
);
}
}
export default reduxForm({
form: 'BasicForm',
enableReinitialize: true,
})(BasicForm);
However modal is rendered using portal, outside current DOM.
As modal is rendered outside the scope of redux context, it is not getting the
store. and i am getting an error "Uncaught Error: Field must be inside a component decorated with reduxForm()"
Below is link to same kind of problem, where redux form within portal is not working.
Redux Form Wrapped Inside Custom Portal Component?
in React 16 it is handled by portals, but version before then that you can try something like as follow.
export default class ExampleBasic extends PureComponent {
...
static contextTypes = { store: React.PropTypes.object };
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<Provider store={this.context.store}>
<BasicFormContainer />
</Provider>
</Modal>
)}
</div>
);
}
}
You need to pass in the values of BasicForm.js to the Redux store and dispatch an action from there itself and not from the BasicFormContainer.js. This way, the Modal remains inside of the scope of your root element and thus there is no need to access the store outside of the Provider.
Then update the Redux store based on the values entered in the form. Once, the store is updated, you can then access it from anywhere in your application such as Modal in your case.
I downgraded to version 2.1.0 to solve the problem.

Console error whilst waiting for API response - React/Redux

I am fetching data from a remote API in componentDidMount:
componentDidMount() {
this.props.fetchRemoteData('photos')
}
And then the received data is passed to my component props in mapStateToProps, using a selector to filter a specific object from the received array:
const mapStateToProps = (state, { params }) => {
const photoId = parseInt(params.photoId)
return {
singlePhoto: getSinglePhoto(state.filteredList.photos.jsonArray, photoId),
isFetching: state.filteredList.photos.isFetching
}
}
The content renders, but there is a split second before that, where it seems to be trying to the render the content before the data is successfully retrieved, which brings up the following error in the console:
Uncaught TypeError: Cannot read property 'charAt' of undefined
undefined is here referring to this.props.singlePhoto. But when singlePhoto receives the data payload the content renders.
Here is my container component:
class PhotoSingle extends Component {
componentDidMount() {
this.props.fetchRemoteData('photos')
}
render() {
const {singlePhoto, isFetching} = this.props
const photoTitle = capitalizeFirstLetter(singlePhoto.title)
return (
<div>
<PhotoSingleImg singlePhoto={singlePhoto} photoTitle={photoTitle} isFetching={isFetching}/>
</div>
)
}
}
const mapStateToProps = (state, { params }) => {
const photoId = parseInt(params.photoId)
return {
singlePhoto: getSinglePhoto(state.filteredList.photos.jsonArray, photoId),
isFetching: state.filteredList.photos.isFetching
}
}
import * as actions from '../actions/actionCreators'
PhotoSingle = connect(mapStateToProps, actions)(PhotoSingle)
export default PhotoSingle;
And my presentational component:
const PhotoSingleImg = ({ singlePhoto, photoTitle, isFetching }) => {
if (isFetching) {
return <h4>Fetching data...</h4>
}
return (
<div>
<h1>Single Photo</h1>
<h3>Title</h3>
<hr />
<img className='single-photo' src={singlePhoto.url} />
<p>Album ID: {singlePhoto.albumId} | Photo ID: {singlePhoto.id}</p>
</div>
)
}
export default PhotoSingleImg;
I'm unsure how to make it so the content will only attempt to render after I the API response has been received.
Any help appreciated.
Have you defined initial state in redux store?
You can try this way:
return singlePhoto ?
(<div>
<h1>Single Photo</h1>
<h3>Title</h3>
<hr />
<img className='single-photo' src={singlePhoto.url} />
<p>Album ID: {singlePhoto.albumId} | Photo ID: {singlePhoto.id}</p>
</div>) : null

Resources