How to set null value for AutocompleteInput/ReferenceInput? - admin-on-rest

I AutocompleteInput wrapped ReferenceInput. In my case One Project has many Accounts. On edit account page I set project from available and save.
<ReferenceInput source="project_id" reference="projects" allowEmpty filterToQuery={searchText => ({ query_content: searchText })}>
<AutocompleteInput optionText="title" />
</ReferenceInput>
And now I need to set null for value project_id. It can even button which I could place near AutocompleteInput, but I don't know how set value straight to redux. Preferably I would like to avoid special http-request to API to reset this field.
Thanks!

i just took AutocompleteInput and append to it button to clear field. and call NullableAutocompleteInput. then also as earlier paste to ReferenceInput
<ReferenceInput source="field_id" reference="resources" allowEmpty filterToQuery={searchText => ({ query_title: searchText })}>
<NullableAutocompleteInput optionText={(choice) => optionRenderer(choice, 'Resource', 'title')} />
</ReferenceInput>
and whole code component
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import AutoComplete from 'material-ui/AutoComplete';
import BackspaceIcon from 'material-ui/svg-icons/content/backspace';
import IconButton from 'material-ui/IconButton';
import {FieldTitle} from 'admin-on-rest';
import {translate} from 'admin-on-rest';
export class NullableAutocompleteInput extends Component {
handleNewRequest = (chosenRequest, index) => {
if (index !== -1) {
const { choices, input, optionValue } = this.props;
input.onChange(choices[index][optionValue]);
}
}
getSuggestion(choice) {
const { optionText, translate, translateChoice } = this.props;
const choiceName = typeof optionText === 'function' ? optionText(choice) : choice[optionText];
return translateChoice ? translate(choiceName, { _: choiceName }) : choiceName;
}
clearData() {
this.props.input.onChange(null);
}
render() {
const {
choices,
elStyle,
filter,
input,
isRequired,
label,
meta: { touched, error },
options,
optionValue,
resource,
setFilter,
source,
} = this.props;
const selectedSource = choices.find(choice => choice[optionValue] === input.value);
const dataSource = choices.map(choice => ({
value: choice[optionValue],
text: this.getSuggestion(choice),
}));
return (
<div>
<AutoComplete
searchText={selectedSource && this.getSuggestion(selectedSource)}
dataSource={dataSource}
floatingLabelText={<FieldTitle label={label} source={source} resource={resource} isRequired={isRequired} />}
filter={filter}
onNewRequest={this.handleNewRequest}
onUpdateInput={setFilter}
openOnFocus
style={elStyle}
errorText={touched && error}
{...options}
/>
<IconButton onTouchTap={this.clearData.bind(this)} tooltip="Clear Data" tooltipPosition="top-right">
<BackspaceIcon color='grey' hoverColor='black'/>
</IconButton>
</div>
);
}
}
NullableAutocompleteInput.propTypes = {
addField: PropTypes.bool.isRequired,
choices: PropTypes.arrayOf(PropTypes.object),
elStyle: PropTypes.object,
filter: PropTypes.func.isRequired,
input: PropTypes.object,
isRequired: PropTypes.bool,
label: PropTypes.string,
meta: PropTypes.object,
options: PropTypes.object,
optionElement: PropTypes.element,
optionText: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func,
]).isRequired,
optionValue: PropTypes.string.isRequired,
resource: PropTypes.string,
setFilter: PropTypes.func,
source: PropTypes.string,
translate: PropTypes.func.isRequired,
translateChoice: PropTypes.bool.isRequired,
};
NullableAutocompleteInput.defaultProps = {
addField: true,
choices: [],
filter: AutoComplete.fuzzyFilter,
options: {},
optionText: 'name',
optionValue: 'id',
translateChoice: true,
};
export default translate(NullableAutocompleteInput);

Related

Getting an error "A non-serializable value was detected in an action, in the path: `payload`. Value: " in react-redux

I wrote a function in add user and remove user on react-redux. But i getting error on this function
user Slice.js in redux
import { createSlice } from "#reduxjs/toolkit";
const userSlice = createSlice({
name: "users",
initialState: {
users: {
id: "",
name: "",
email: "",
},
},
reducers: {
addUser: (state, action) => {
state.users = action.payload;
},
removeUser: (state) => {
state.users = "";
},
},
});
export const { addUser, removeUser } = userSlice.actions;
export default userSlice.reducer;
The remove user funcion is returning a empty string. on the screen value is empty.
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { removeUser } from "../Slices/userSlice";
import {} from "react-redux";
const User = () => {
const users = useSelector((state) => state.users.users);
const dispatch = useDispatch();
console.log(users);
const removeuser = (state) => {
dispatch(removeUser(state));
};
return (
<div>
<h1>{users.id}</h1>
<h2>{users.name}</h2>
<h3>{users.email}</h3>
<button type="" onClick={removeuser}>
Remove User
</button>
</div>
);
};
export default User;
what the solution of this function
What I am seeing in your error message is that you are passing a React event in your action payload.
Why is this happening? An onClick prop is a function which gets called with the click event as its argument.
<button type="" onClick={removeuser}>
So the argument of removeuser needs to be (event) => {. Or if you don't need to access the event then you can use () => {. There is no scenario in which a state variable makes sense as an argument.
When you call a Redux Toolkit action creator function, the argument that you pass to the function will become the action.payload.
With the way that your slice is set up now, the removeUser reducer does not use the action.payload so it does not need an argument. You just call dispatch(removerUser()).
const onClickRemove = () => {
dispatch(removeUser());
};
/* ... */
<button onClick={onClickRemove}>
or
<button onClick={() => dispatch(removeUser())}>
Does you app have more than one user? As you begin to understand things more, your userSlice will probably evolve. You may end up with an array of users, in which case your removeUser action will need to know which user to remove.
Something like:
import { createSlice } from "#reduxjs/toolkit";
const userSlice = createSlice({
name: "users",
initialState: {
users: []
},
reducers: {
// Payload is an array of user objects.
setAllUsers: (state, action) => {
state.users = action.payload;
},
// Payload is a single user object.
addUser: (state, action) => {
state.users.push(action.payload);
},
// Payload is the id of the user to remove.
removeUser: (state, action) => {
state.users = state.users.filter(user => user.id !== action.payload);
},
},
});
export const { setAllUsers, addUser, removeUser } = userSlice.actions;
export default userSlice.reducer;
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { removeUser } from "../Slices/userSlice";
const UsersList = () => {
const users = useSelector((state) => state.users.users);
console.log(users);
return (
<div>
{users.map(user => (
<UserListItem key={user.id} user={user}/>
))}
</div>
);
}
const UserListItem = ({ user }) => {
const dispatch = useDispatch();
return (
<div>
<h1>{user.id}</h1>
<h2>{user.name}</h2>
<h3>{user.email}</h3>
<button onClick={() => dispatch(removeUser(user.id))}>
Remove User
</button>
</div>
);
};
export default UsersList;

How to test react component mocking fetch api?

I'm trying to test a react component where a fetch call occurs. The component:
class SearchResults extends React.Component<{ tag: string; setSpinner: (value: boolean) => void }> {
state: searchResultsState = {
results: [],
tag: '',
cardFull: null,
};
constructor(props: { tag: string; setSpinner: (value: boolean) => void }) {
super(props);
this.handleCardModal = this.handleCardModal.bind(this);
}
componentDidMount() {
getResponse(
this.props.tag,
(res) => {
this.setState({ results: res, tag: this.props.tag });
},
this.props.setSpinner
);
}
componentDidUpdate(prevProps: { tag: string }) {
if (this.props.tag !== prevProps.tag) {
getResponse(
this.props.tag,
(res) => this.setState({ results: res, tag: this.props.tag }),
this.props.setSpinner
);
}
}
handleCardModal() {
this.setState({ cardFull: null });
}
render() {
return (
<Fragment>
{this.state.cardFull && (
<CardFull data={this.state.cardFull} handleClick={this.handleCardModal} />
)}
{this.state.cardFull && <div className="blackout" onClick={this.handleCardModal} />}
<div className="search-results">
{this.state.results.length === 0 && this.state.tag !== '' && <div>Sorry, no matched</div>}
{this.state.results.map((result) => (
<CardUI
key={result.id}
className="card"
variant="outlined"
sx={{ minWidth: 275 }}
onClick={() => {
const currentCard = this.state.results.filter((res) => res.id === result.id)[0];
this.setState({ ...this.state, cardFull: currentCard });
}}
>
<CardMedia component="img" height="194" image={getUrl(result)} alt={result.title} />
<CardContent>
<p>{result.title}</p>
</CardContent>
</CardUI>
))}
</div>
</Fragment>
);
}
}
First I tried to use jest-fetch-mock.
import '#testing-library/jest-dom';
import { render } from '#testing-library/react';
import renderer from 'react-test-renderer';
import SearchResults from '../../src/components/SearchResults/SearchResults';
import sampleSearchResults from '../__fixtures__/sampleSearchResults';
import fetch from 'jest-fetch-mock';
fetch.enableMocks();
beforeEach(() => {
fetch.resetMocks();
});
const setSpinner = jest.fn();
describe('Search Results component', () => {
fetch.mockResponseOnce(JSON.stringify({ photos: sampleSearchResults }));
test('Search Results matches snapshot', () => {
const searchResults = renderer
.create(<SearchResults tag={''} setSpinner={setSpinner} />)
.toJSON();
expect(searchResults).toMatchSnapshot();
});
test('search results renders correctly', () => {
render(<SearchResults setSpinner={setSpinner} tag={'dove'} />);
});
});
But it gives the error during tests:
console.error
FetchError {
message: 'invalid json response body at reason: Unexpected end of JSON input',
type: 'invalid-json'
}
So, I've decided to mock fetch manually
import React from 'react';
import '#testing-library/jest-dom';
import { render, screen } from '#testing-library/react';
import renderer from 'react-test-renderer';
import SearchResults from '../../src/components/SearchResults/SearchResults';
import sampleSearchResults from '../__fixtures__/sampleSearchResults';
const setSpinner = jest.fn();
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ photos: sampleSearchResults }),
})
) as jest.Mock;
describe('Search Results component', () => {
test('Search Results matches snapshot', () => {
const searchResults = renderer
.create(<SearchResults tag={''} setSpinner={setSpinner} />)
.toJSON();
expect(searchResults).toMatchSnapshot();
});
test('search results renders correctly', () => {
render(<SearchResults setSpinner={setSpinner} tag={'dove'} />);
const title = screen.getByText(/Eurasian Collared Dove (Streptopelia decaocto)/i);
expect(title).toBeInTheDocument(); //mistake
});
});
Now fetch mock works correct, but it renders only one div - search results and doesn't render card. How can I test my component? Thank you.

Using Form.Select from React Semantic UI with React-hooks

as the question suggests, I'm using RSUI with React-Hooks in a Next.js project and I'm trying to figure out how to send a payload from a Form.Select to a graphql endpoint. I've included a lot of extra code for context but really what I'm after is to successfully set "security_type" using setValues
import React, { useState } from 'react'
import Router from 'next/router'
import { Button, Checkbox, Form, Segment, Table } from 'semantic-ui-react'
import Layout from '../../components/layout'
import Loading from '../../components/loading'
import Error from '../../components/error'
import { useFetchUser } from '../../lib/user'
import { useQuery, useMutation } from '#apollo/react-hooks';
import query from '../../graphql/project/query';
import mutation from '../../graphql/project/mutation';
const security_types = [
{ key: 'Bank Guarantee', value: 'Bank Guarantee', text: 'Bank Guarantee', name: 'Bank Guarantee' },
{ key: 'Cash Retention', value: 'Cash Retention', text: 'Cash Retention', name: 'Cash Retention' },
{ key: 'Surety Bond', value: 'Surety Bond', text: 'Surety Bond', name: 'Surety Bond' },
];
function CreateProject() {
const { loading, error, data } = useQuery(query);
const [createProject] = useMutation(mutation,
{
onCompleted(data) {
Router.replace("/create_project", "/project/" + data.createProject.project_id, { shallow: true });
}
});
const { user, user_loading } = useFetchUser()
let [form, setValues] = useState({
project_number: '',
project_name: '',
security_type: '',
});
let updateField = e => {
console.log('e: ', e)
setValues({
...form,
[e.target.name]: e.target.value
});
};
let mutationData = ''
if (loading) return <Loading />;
if (error) return <Error />;
return (
<Layout user={user} user_loading={user_loading}>
<h1>Let's create a project</h1>
{user_loading ? <>Loading...</> :
<div>
<Segment>
<Form
onSubmit={e => {
e.preventDefault();
console.log('form: ', form)
createProject({ variables: { ...form } });
form = '';
}}>
<Form.Input
fluid
label='Project Number'
name="project_number"
value={form.project_number}
placeholder="Project Number"
onChange={updateField}
/>
<Form.Input
fluid
label='Project Name'
name="project_name"
value={form.project_name}
onChange={updateField}
/>
<Form.Select
fluid
selection
label='Security Type'
options={security_types}
placeholder='Security Type'
name="security_type"
value={form.security_type}
onChange={(e, { value }) => setValues(console.log('value: ', value), { "security_type": value })}
/>
<Button>Submit</Button>
</Form>
</Segment>
</div>
}
</Layout>
);
}
export default CreateProject;
I think all my troubles relate to the onChange section so any help would be great.

Custom List component - issues on modules import

I'm making a custom List component with a custom empty list message. But refresh and actions are not executed any more. I suspect I do not import properly 'DefaultActions' & 'refreshViewAction', do you see the issue?
I dont understand what is the issue on my imports as I took the 'admin-on-rest' List version, changed imports and customized it.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { parse, stringify } from 'query-string';
import { push as pushAction } from 'react-router-redux';
import { Card, CardText } from 'material-ui/Card';
import compose from 'recompose/compose';
import { createSelector } from 'reselect';
import inflection from 'inflection';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import autoprefixer from 'material-ui/utils/autoprefixer';
import { queryReducer } from 'admin-on-rest';
/*
import queryReducer, {
SET_SORT,
SET_PAGE,
SET_FILTER,
SORT_DESC,
} from '../../reducer/admin/resource/list/queryReducer';
*/
//import ViewTitle from '../layout/ViewTitle';
import { ViewTitle } from 'admin-on-rest';
//import Title from '../layout/Title';
import { Title } from 'admin-on-rest';
//import DefaultPagination from './Pagination';
import { Pagination as DefaultPagination } from 'admin-on-rest';
//import DefaultActions from './Actions';
// TODO : Bug on ref import here
import { Actions as DefaultActions } from 'admin-on-rest';
//import { crudGetList as crudGetListAction } from '../../actions/dataActions';
import { crudGetList as crudGetListAction } from 'admin-on-rest';
//import { changeListParams as changeListParamsAction } from '../../actions/listActions';
import { changeListParams as changeListParamsAction } from 'admin-on-rest';
//import { refreshView as refreshViewAction } from '../../actions/uiActions';
// TODO : Bug on ref import here
import { refreshView as refreshViewAction } from 'admin-on-rest/lib/actions/uiActions';
//import translate from '../../i18n/translate';
import { translate } from 'admin-on-rest';
//import removeKey from '../../util/removeKey';
import { removeKey } from 'admin-on-rest';
//import defaultTheme from '../defaultTheme';
import { defaultTheme } from 'admin-on-rest';
//import withPermissionsFilteredChildren from '../../auth/withPermissionsFilteredChildren';
import withPermissionsFilteredChildren from 'admin-on-rest/lib/auth/withPermissionsFilteredChildren';
import { LinkToUpload } from './ListActions'
import { WithPermission } from 'admin-on-rest'
import authClient from '../../authClient'
const { SET_SORT,
SET_PAGE,
SET_FILTER,
SORT_DESC, } = queryReducer
const styles = {
noResults: { padding: 20 },
header: {
display: 'flex',
justifyContent: 'space-between',
},
};
/**
* List page component
*
* The <List> component renders the list layout (title, buttons, filters, pagination),
* and fetches the list of records from the REST API.
* It then delegates the rendering of the list of records to its child component.
* Usually, it's a <Datagrid>, responsible for displaying a table with one row for each post.
*
* In Redux terms, <List> is a connected component, and <Datagrid> is a dumb component.
*
* Props:
* - title
* - perPage
* - sort
* - filter (the permanent filter to apply to the query)
* - actions
* - filters (a React Element used to display the filter form)
* - pagination
*
* #example
* const PostFilter = (props) => (
* <Filter {...props}>
* <TextInput label="Search" source="q" alwaysOn />
* <TextInput label="Title" source="title" />
* </Filter>
* );
* export const PostList = (props) => (
* <List {...props}
* title="List of posts"
* sort={{ field: 'published_at' }}
* filter={{ is_published: true }}
* filters={<PostFilter />}
* >
* <Datagrid>
* <TextField source="id" />
* <TextField source="title" />
* <EditButton />
* </Datagrid>
* </List>
* );
*/
export class List extends Component {
state = {};
componentDidMount() {
this.updateData();
if (Object.keys(this.props.query).length > 0) {
this.props.changeListParams(this.props.resource, this.props.query);
}
}
componentWillReceiveProps(nextProps) {
if (
nextProps.resource !== this.props.resource ||
nextProps.query.sort !== this.props.query.sort ||
nextProps.query.order !== this.props.query.order ||
nextProps.query.page !== this.props.query.page ||
nextProps.query.filter !== this.props.query.filter
) {
this.updateData(
Object.keys(nextProps.query).length > 0
? nextProps.query
: nextProps.params
);
}
if (nextProps.version !== this.props.version) {
this.updateData();
}
}
shouldComponentUpdate(nextProps, nextState) {
if (
nextProps.isLoading === this.props.isLoading &&
nextProps.width === this.props.width &&
nextProps.version === this.props.version &&
nextState === this.state
) {
return false;
}
return true;
}
getBasePath() {
return this.props.location.pathname.replace(/\/$/, '');
}
/**
* Merge list params from 3 different sources:
* - the query string
* - the params stored in the state (from previous navigation)
* - the props passed to the List component
*/
getQuery() {
const query =
Object.keys(this.props.query).length > 0
? this.props.query
: { ...this.props.params };
if (!query.sort) {
query.sort = this.props.sort.field;
query.order = this.props.sort.order;
}
if (!query.perPage) {
query.perPage = this.props.perPage;
}
return query;
}
updateData(query) {
const params = query || this.getQuery();
const { sort, order, page, perPage, filter } = params;
const pagination = {
page: parseInt(page, 10),
perPage: parseInt(perPage, 10),
};
const permanentFilter = this.props.filter;
this.props.crudGetList(
this.props.resource,
pagination,
{ field: sort, order },
{ ...filter, ...permanentFilter }
);
}
setSort = sort => this.changeParams({ type: SET_SORT, payload: sort });
setPage = page => this.changeParams({ type: SET_PAGE, payload: page });
setFilters = filters =>
this.changeParams({ type: SET_FILTER, payload: filters });
showFilter = (filterName, defaultValue) => {
this.setState({ [filterName]: true });
if (typeof defaultValue !== 'undefined') {
this.setFilters({
...this.props.filterValues,
[filterName]: defaultValue,
});
}
};
hideFilter = filterName => {
this.setState({ [filterName]: false });
const newFilters = removeKey(this.props.filterValues, filterName);
this.setFilters(newFilters);
};
changeParams(action) {
const newParams = queryReducer(this.getQuery(), action);
this.props.push({
...this.props.location,
search: `?${stringify({
...newParams,
filter: JSON.stringify(newParams.filter),
})}`,
});
this.props.changeListParams(this.props.resource, newParams);
}
refresh = () => {
if (process.env !== 'production') {
console.warn( // eslint-disable-line
'Deprecation warning: The preferred way to refresh the List view is to connect your custom button with redux and dispatch the `refreshView` action.'
);
}
this.props.refreshView();
};
render() {
const {
children,
filters,
pagination = <DefaultPagination />,
actions = <DefaultActions />,
resource,
hasCreate,
title,
data,
ids,
total,
isLoading,
translate,
theme,
version,
} = this.props;
const query = this.getQuery();
const filterValues = query.filter;
const basePath = this.getBasePath();
const resourceName = translate(`resources.${resource}.name`, {
smart_count: 2,
_: inflection.humanize(inflection.pluralize(resource)),
});
const defaultTitle = translate('aor.page.list', {
name: `${resourceName}`,
});
const titleElement = (
<Title title={title} defaultTitle={defaultTitle} />
);
const muiTheme = getMuiTheme(theme);
const prefix = autoprefixer(muiTheme);
return (
<div className="list-page">
<Card style={{ opacity: isLoading ? 0.8 : 1 }}>
<div style={prefix(styles.header)}>
<ViewTitle title={titleElement} />
{actions &&
React.cloneElement(actions, {
resource,
filters,
filterValues,
basePath,
hasCreate,
displayedFilters: this.state,
showFilter: this.showFilter,
theme,
refresh: this.refresh,
})}
</div>
{filters &&
React.cloneElement(filters, {
resource,
hideFilter: this.hideFilter,
filterValues,
displayedFilters: this.state,
setFilters: this.setFilters,
context: 'form',
})}
{isLoading || total > 0 ? (
<div key={version}>
{children &&
React.cloneElement(children, {
resource,
ids,
data,
currentSort: {
field: query.sort,
order: query.order,
},
basePath,
isLoading,
setSort: this.setSort,
})}
{pagination &&
React.cloneElement(pagination, {
total,
page: parseInt(query.page, 10),
perPage: parseInt(query.perPage, 10),
setPage: this.setPage,
})}
</div>
) : (
<CardText style={styles.noResults}>
You haven't uploaded the workforce database yet.
<WithPermission value={['admin', 'project-leader']} authClient={authClient} >
<LinkToUpload _projectId={filterValues._projectId} />
</WithPermission>
</CardText>
)}
</Card>
</div>
);
}
}
List.propTypes = {
// the props you can change
title: PropTypes.any,
filter: PropTypes.object,
filters: PropTypes.element,
pagination: PropTypes.element,
actions: PropTypes.element,
perPage: PropTypes.number.isRequired,
sort: PropTypes.shape({
field: PropTypes.string,
order: PropTypes.string,
}),
children: PropTypes.node,
// the props managed by admin-on-rest
authClient: PropTypes.func,
changeListParams: PropTypes.func.isRequired,
crudGetList: PropTypes.func.isRequired,
data: PropTypes.object, // eslint-disable-line react/forbid-prop-types
filterValues: PropTypes.object, // eslint-disable-line react/forbid-prop-types
hasCreate: PropTypes.bool.isRequired,
ids: PropTypes.array,
isLoading: PropTypes.bool.isRequired,
location: PropTypes.object.isRequired,
path: PropTypes.string,
params: PropTypes.object.isRequired,
push: PropTypes.func.isRequired,
query: PropTypes.object.isRequired,
resource: PropTypes.string.isRequired,
refreshView: PropTypes.func.isRequired,
total: PropTypes.number.isRequired,
translate: PropTypes.func.isRequired,
theme: PropTypes.object.isRequired,
version: PropTypes.number.isRequired,
};
List.defaultProps = {
filter: {},
filterValues: {},
perPage: 10,
sort: {
field: 'id',
order: SORT_DESC,
},
theme: defaultTheme,
};
const getLocationSearch = props => props.location.search;
const getQuery = createSelector(getLocationSearch, locationSearch => {
const query = parse(locationSearch);
if (query.filter && typeof query.filter === 'string') {
query.filter = JSON.parse(query.filter);
}
return query;
});
function mapStateToProps(state, props) {
const resourceState = state.admin.resources[props.resource];
return {
query: getQuery(props),
params: resourceState.list.params,
ids: resourceState.list.ids,
total: resourceState.list.total,
data: resourceState.data,
isLoading: state.admin.loading > 0,
filterValues: resourceState.list.params.filter,
version: state.admin.ui.viewVersion,
};
}
const enhance = compose(
connect(mapStateToProps, {
crudGetList: crudGetListAction,
changeListParams: changeListParamsAction,
push: pushAction,
refreshView: refreshViewAction,
}),
translate,
withPermissionsFilteredChildren
);
export default enhance(List);
You should put all your imports from admin-on-rest on the same line:
import { ViewTitle, Title, [ALL OTHERS HERE TOO] } from 'admin-on-rest';

react-navigation is not working in react-native actions

Please check my code below i have to navigate to EmployeeCreate after login success .I use this in dispatch(NavigationActions.navigate({ routeName: 'EmployeeCreate' })); in my AuthActions.js. but not working.Previously i used 'react-native-router-flux' instead of react-navigation. Iam new to react-native and couldn't find the issue.
App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import firebase from 'firebase';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import Router from './Router';
class App extends Component {
componentWillMount() {
const config = {
apiKey: "###",
authDomain: "###",
databaseURL: "###",
projectId: "###",
storageBucket: "###,
messagingSenderId: "0000000"
};
firebase.initializeApp(config);
}
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Router />
</Provider>
);
}
}
export default App;
Router.js
import React from 'react';
import { StackNavigator} from 'react-navigation';
import LoginForm from './components/LoginForm';
import EmployeeList from './components/EmployeeList';
import EmployeeCreate from './components/EmployeeCreate';
import EmployeeEdit from './components/EmployeeEdit';
const RouterComponent = StackNavigator({
LoginForm : {screen : LoginForm},
EmployeeCreate : {screen :EmployeeCreate},
EmployeeEdit:{screen:EmployeeEdit},
},
{
headerMode : 'none',
navigationOptions:{
headerVisible : false,
}
}
)
export default RouterComponent;
AuthActions.js
import firebase from 'firebase';
import { NavigationActions } from 'react-navigation'
import {
EMAIL_CHANGED,
PASSWORD_CHANGED,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAIL,
LOGIN_USER
} from './types';
export const emailChanged = (text) => {
return {
type: EMAIL_CHANGED,
payload: text
};
};
export const passwordChanged = (text) => {
return {
type: PASSWORD_CHANGED,
payload: text
};
};
export const loginUser = ({ email, password }) => {
return (dispatch) => {
dispatch({ type: LOGIN_USER });
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch((error) => {
console.log(error);
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(user => loginUserSuccess(dispatch, user))
.catch(() => loginUserFail(dispatch));
});
};
};
const loginUserFail = (dispatch) => {
dispatch({ type: LOGIN_USER_FAIL });
};
const loginUserSuccess = (dispatch, user) => {
dispatch({
type: LOGIN_USER_SUCCESS,
payload: user
});
dispatch(NavigationActions.navigate({ routeName: 'EmployeeCreate' }));
};
AuthReducer.js
import {
EMAIL_CHANGED,
PASSWORD_CHANGED,
LOGIN_USER_SUCCESS,
LOGIN_USER_FAIL,
LOGIN_USER
} from '../actions/types';
const INITIAL_STATE = {
email: '',
password: '',
user: null,
error: '',
loading: false
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case EMAIL_CHANGED:
return { ...state, email: action.payload };
case PASSWORD_CHANGED:
return { ...state, password: action.payload };
case LOGIN_USER:
return { ...state, loading: true, error: '' };
case LOGIN_USER_SUCCESS:
return { ...state, ...INITIAL_STATE, user: action.payload };
case LOGIN_USER_FAIL:
return { ...state, error: 'Authentication Failed.', password: '', loading: false };
default:
return state;
}
};
You are using dispatch method of Redux. You should be using the dispatch method of React-navigation instead. You can do one of these:
1) Do this.props.navigation.dispatch(NavigationActions.navigate({ routeName: 'EmployeeCreate' }))
2) Or integrate React-navigation with Redux ( recommended for your case especially ) and use Redux's default dispatch. I have created a barebones repo just for react-navigation + Redux
React navigation with Redux flow is changed little bit in newer versions of React Navigation.
You can checkout the best way to do it from 3.x in their documentation itself.
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html

Resources