I am trying to make an async react-select component with redux but somehow not able to get search results in the dropdown. Very new to this. Please help :)
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import Select from 'react-select';
import { fetchInstitutionsIfNeeded } from '../../actions/institutions';
class Signup extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null
};
this.getInstitutions = this.getInstitutions.bind(this);
this.onChange = this.onChange.bind(this);
}
onChange(input) {
this.setState({
value: input
});
}
getInstitutions(input) {
const { dispatch } = this.props;
if (!input) {
return Promise.resolve({ options: [] });
}
dispatch(fetchInstitutionsIfNeeded(input));
}
render() {
let options = this.props.options;
return (
<div>
<Select.Async
name="institute"
value={this.state.value}
autoload={false}
onChange={this.onChange}
loadOptions={this.getInstitutions}
/>
</div>
);
}
}
const mapStateToProps = (state) => ({
options: state.institutions.options
});
export default connect(mapStateToProps)(Signup);
Also options object is also properly formatted and is getting updated properly in redux store but not reflecting back in select async's dropdown.
Try this, we can also return from action but it breaks the whole idea of reducers.
// actions.js
export const getProducts = (input = '') => async (dispatch) => {
dispatch({
type: GET_PRODUCTS_PENDING,
payload: {},
});
try {
const response = await axios.get(`/api/v1/products/`);
dispatch({
type: GET_PRODUCTS_SUCCESS,
payload: response.data,
});
} catch (error) {
// handle errors
}
};
// components.jsx
class Signup extends PureComponent {
async getProductsForSelect(input) {
await this.props.getProducts(input);
return this.props.product.options;
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit}>
<AsyncSelect
isMulti
cacheOptions
defaultOptions
loadOptions={(e) => this.getProductsForSelect(e)}
/>
</form>
);
}
}
Related
Hello i am new to programming, and i have learn to use MERN to make a sign up,i have no issue with backend but when i tried to use redux ,i have this problem in the frontend
enter image description here
This is the code for the SignInScreen.js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
I dont know what wrong with the code,but i do know that it connected with the redux store which have reducer,action and constant..this is for the redux store.js
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import {
userLoginReducer,
} from './reducers/userReducer'
const userInfo = localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null
const initialState = {
userLogin: { userInfo },
}
const reducer = combineReducers({
userLogin: userLoginReducer,
})
const middleware = [thunk]
const store = createStore(
reducer,
initialState,
compose(applyMiddleware(...middleware))
)
export default store
This is for constant
export const USER_LOGIN_REQUEST = 'USER_LOGIN_REQUEST'
export const USER_LOGIN_SUCCESS = 'USER_LOGIN_SUCCESS'
export const USER_LOGIN_FAIL = 'USER_LOGIN_FAIL'
export const USER_LOGOUT = 'USER_LOGOUT'
this is userReducer.js
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
USER_LOGOUT,
} from '../constants/userConstant'
function userLoginReducer(state = {}, action) {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true }
case USER_LOGIN_SUCCESS:
return { loading: false, userInfo: action.payload }
case USER_LOGIN_FAIL:
return { loading: false, error: action.payload }
case USER_LOGOUT:
return {}
default:
return state
}
}
export userLoginReducer
and lastly for user.js
import Axios from 'axios'
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAIL,
} from '../constants/userConstant'
const login = (email, password) => async (dispatch) => {
try {
dispatch({ type: USER_LOGIN_REQUEST })
const config = { headers: { 'Content-Type': 'application/json' } }
const { data } = await Axios.post(
'/api/users/login',
{ email, password },
config
)
dispatch({ type: USER_LOGIN_SUCCESS, payload: data })
localStorage.setItem('userInfo', JSON.stringify(data))
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
export login
I just want to get the data into the userInfo but it dont recognized them and said it is a TypeError..I hope u can help me with this..im using the redux latest version
The problem is in userLoginReducer. Each reducer should return a complete new copy of the store.
If you return just the changes, the object you return replaces the entire state.
For example in this code:
switch (action.type) {
case USER_LOGIN_REQUEST:
return { loading: true };
}
The state { userLogin: { userInfo } } will be replaced with { loading: true }. Then you will not have userLogin anymore in the state. That's why you get the error.
To overcome this problem, spread the previous state in returned object (for all actions):
switch (action.type) {
case USER_LOGIN_REQUEST:
return { ....state, loading: true }; // ...state copies exist state to the new copy of state
}
Note: To easily solve similar bugs in the future, I recommend to use redux devtools extension. It is a great extension for debugging and look at changes in redux store.
I have tried that but still cannot fix my problem,this is the rest of my signinScreen,js
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { login } from '../actions/userAction'
export const SignInScreen = (props, history) => {
const navigate = useNavigate
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const submitHandler = (e) => {
e.preventDefault()
dispatch(login(email, password))
}
const { search } = useLocation()
const redirectInUrl = new URLSearchParams(search).get('redirect')
const redirect = redirectInUrl ? redirectInUrl : '/'
const userlogin = useSelector((state) => state.userlogin)
const { userInfo, loading, error } = userlogin
const dispatch = useDispatch()
useEffect(() => {
if (userInfo) {
navigate(redirect)
}
}, [navigate, userInfo, redirect])
return (
<Container className="small-container">
<h1 className="my-3">Sign In</h1>
<Form onSubmit={submitHandler}>
<Form.Group className="mb-3" controlId="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
required
onChange={(e) => setEmail(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
required
onChange={(e) => setPassword(e.target.value)}
/>
</Form.Group>
<div className="mb-3">
<Button type="submit">Sign In</Button>
</div>
<div className="mb-3">
New Customer?{' '}
<Link to={`/signup?redirect=${redirect}`}>Create new account</Link>
</div>
</Form>
</Container>
)
}
it still show the same error like before,i have no idea to solve this
I am studying react-redux. First of all, the action of addTodo looks like this.
let nextTodoId = 0;
export const addTodo = (content) => ({
type: ADD_TODO,
payload: {
id: ++nextTodoId,
content
}
});
And the component is here
import React, { useState } from "react";
import { connect } from "react-redux";
import { addTodo } from "../redux/actions";
function AddTodoComponent(props) {
const [inputValue, setInput] = useState("");
console.log({ addTodo })
const handleAddTodo = () => {
console.log(inputValue)
props.addTodo(inputValue);
setInput("");
};
return (
<div>
<input onChange={(e) => setInput(e.target.value)} />
<button className="add-todo" onClick={handleAddTodo}>
Add Todo
</button>
</div>
);
}
export default connect(null, { addTodo })(AddTodoComponent);
I want to use mapStateToProps and mapDispatchToProps but It's not working.
I thought It will be work if I use destructing object.
But it's not appear in console too.
Am I wrong?
import React, { useState } from "react";
import { connect } from "react-redux";
import { addTodo } from "../redux/actions";
function AddTodo({
allId,
byId,
newTodo,
}) {
const [inputValue, setInput] = useState("");
const handleAddTodo = () => {
newTodo(inputValue);
setInput("");
};
return (
<div>
<input onChange={(e) => setInput(e.target.value)} />
<button className="add-todo" onClick={handleAddTodo}>
Add Todo
</button>
</div>
);
}
const mapStateToProps = state => {
return {
allId: state.allId,
byId: state.byId,
}
}
const mapDispatchToProps = dispatch => {
return {
newTodo: () => dispatch(addTodo()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);
and this is reducer function.
import { ADD_TODO, TOGGLE_TODO } from "../actionTypes";
const initialState = {
allIds: [],
byIds: {}
};
export default function(state = initialState, action) {
switch (action.type) {
case ADD_TODO: {
const { id, content } = action.payload;
return {
...state,
allIds: [...state.allIds, id],
byIds: {
...state.byIds,
[id]: {
content,
completed: false
}
}
};
}
case TOGGLE_TODO: {
const { id } = action.payload;
return {
...state,
byIds: {
...state.byIds,
[id]: {
...state.byIds[id],
completed: !state.byIds[id].completed
}
}
};
}
default:
return state;
}
}
It took me a bit of playing but I found your problem! (3 problems, actually).
Your addTodo function is a function which takes the content of the Todo (a string) and creates the action.
const mapDispatchToProps = (dispatch) => {
return {
newTodo: () => dispatch(addTodo())
};
};
But in your mapDispatchToProps function, you are accepting no arguments and calling addTodo with no arguments. So your Todo gets added, but the content will always be undefined.
Change this to
newTodo: (content) => dispatch(addTodo(content))
In mapStateToProps you've got a misnamed property. You need to change state.byId to state.byIds.
In order to clear the content of the input, you need the value of the input to be controlled by your useState. Add value={inputValue} to the input element.
<input value={inputValue} onChange={(e) => setInput(e.target.value)} />
This is a sidenote, but you might want to think about learning typescript and adding annotations to your projects. There is a bit of a learning curve but the tradeoff is you would be able to catch things like missing arguments and misnamed properties easily. They can be a real head-scratcher otherwise.
Hi I'm currently trying to add users to a page using react native, redux, and firebase. When User 1 clicks join, they get added to the feed and likewise for other users. However, a problem I'm facing is when user 2 clicks join, they get added to the feed but don't get displayed on user 1's page unless the user 1 refocuses on the page after going away.
Here is my code for the page itself in react native
import React, { Component } from 'react';
import { Text, View, Button, TouchableOpacity, SafeAreaView, ScrollView, Image } from 'react-native';
import styles from '../styles.js'
import { connect } from 'react-redux'
import { FlatList } from 'react-native-gesture-handler';
import { FontAwesome5 } from '#expo/vector-icons';
import { Octicons } from '#expo/vector-icons';
import { FontAwesome } from '#expo/vector-icons';
import { addUser, removeUser, getLivingRoomUsers } from '../actions/livingRoomUser.js'
import { bindActionCreators } from 'redux'
class LivingRoom extends React.Component {
constructor(props) {
super(props);
this.state = {
inRoom: false,
isMuted: false
};
}
componentDidMount(){
this._unsubscribe = this.props.navigation.addListener('focus', () => {
this.props.getLivingRoomUsers()
});
}
joinRoom = () => {
this.props.addUser()
this.setState({ inRoom: true });
}
leaveRoom = () => {
this.props.removeUser(this.props.livingRoomUser)
this.setState({ inRoom: false });
}
...
render(){
return (
<View>
<SafeAreaView style={styles.livingRoomUserContainer}>
<FlatList
data={this.props.livingRoomUser.feed}
Here is my actions code for the redux portion:
export const addUser = () => {
return async (dispatch, getState) => {
try {
const { user } = getState()
const id = uuid.v4()
const livingRoomUser = {
id: id,
avatar: user.avatar,
username: user.username,
isMuted: false,
date: new Date().getTime(),
}
db.collection('livingroom').doc(id).set(livingRoomUser)
dispatch({
type: 'ADD_USER', payload: livingRoomUser
})
dispatch(getLivingRoomUsers())
} catch (e) {
alert(e)
}
}
}
export const removeUser = (livingRoomUser) => {
return async (dispatch, getState) => {
try {
db.collection('livingroom').doc(livingRoomUser.id).delete();
dispatch(getLivingRoomUsers())
//get living room users
} catch (e) {
alert(e)
}
}
}
export const getLivingRoomUsers = () => {
return async (dispatch, getState) => {
try {
const livingRoomUsers = await db.collection('livingroom').get()
let array = []
livingRoomUsers.forEach((livingRoomUser) => {
array.push(livingRoomUser.data())
})
dispatch({
type: 'GET_LIVING_ROOM_USERS', payload: orderBy(array, 'date', 'asc')
})
} catch (e) {
alert(e)
}
}
}
To summarize. I want the getUsers to be updated anytime someone adds/removes themself from the page. However, from my implementation currently actions only get updated for the current user and the feed only gets updated when the page is focused. How do I go about this?
use onSnapshot listener on the firestore then you can get the latest updates as the store change
export const getLivingRoomUsers = () => {
return async (dispatch, getState) => {
try {
db.collection('livingroom').onSnapshot(snapshot => {
let array = snapshot.docs.map(d => d.data());
dispatch({
type: 'GET_LIVING_ROOM_USERS',
payload: orderBy(array, 'date', 'asc'),
});
});
} catch (e) {
alert(e);
}
};
};
I am new to Redux and I appear to be having an issue. Once my action has been dispatched it is successful however the parent component does not get the updated state until another state change is made. If I click login then delete a character in the input field the state change is then triggered showing me the Menu. Any help/pointers are much appreciated, thanks.
Main (Parent):
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Login from '../login'
import Menu from '../menu'
type Props = { token: string }
class Main extends Component<Props, {}> {
render() {
const { token } = this.props;
if (!token) {
return (
<Login />
)
}
return (
<Menu />
)
}
}
const mapStateToProps = (state) => ({
token: state.session.token,
})
export default connect(
mapStateToProps,
null,
)(Main)
Login (Child):
import React from 'react'
import { connect } from 'react-redux'
import { login } from '../../redux/session/session.actions'
import { View, StyleSheet } from 'react-native'
import { Button, FormLabel, FormInput, FormValidationMessage } from 'react-native-elements'
import styled from 'styled-components/native'
const Container = styled(View)`
flex: 1;
flex-direction: column;
justify-content: center;
align-items: center;
`
const Wrapper = styled(View)`
width: 300;
`
type Props = { login: Function, error: string, loading: boolean };
type State = { email: string, password: string };
class Login extends React.PureComponent<Props, State> {
constructor(props) {
super(props);
this.state = {
email: null,
password: null,
}
}
render() {
console.log('props', this.props);
console.log('state', this.state);
const { loading, error } = this.props;
return (
<Container>
<Wrapper>
<FormValidationMessage>{loading ? 'Loading...' : null}</FormValidationMessage>
<FormValidationMessage>{error ? 'Unable to login, please try again.' : null}</FormValidationMessage>
<FormLabel>Email:</FormLabel>
<FormInput onChangeText={text => this.setState({ email: text })} />
<FormLabel>Password:</FormLabel>
<FormInput secureTextEntry onChangeText={password => this.setState({ password })} />
<Button title='Login' onPress={this.login} />
</Wrapper>
</Container>
)
}
login = () => {
this.props.login(this.state.email, this.state.password);
}
}
const mapStateToProps = (state) => {
console.log(state);
return {
error: state.session.error,
loading: state.session.loading
}
}
const mapDispatchToProps = ({
login
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);
Reducer:
import {
LOGGING_IN,
LOGIN_SUCCESS,
LOGIN_FAILED
} from './session.types'
const initialState = {
loading: null,
error: null,
token: null,
}
export default (state = initialState, { type, payload }) => {
switch (type) {
case LOGGING_IN:
return {
...state,
loading: true
}
case LOGIN_SUCCESS:
return {
...state,
loading: false,
error: null,
token: payload.token
}
case LOGIN_FAILED:
return {
...state,
loading: false,
error: payload.error
}
default:
return state
}
}
Actions:
import { API_URL } from '../../../app-env'
import axios from 'axios'
import {
LOGGING_IN,
LOGIN_SUCCESS,
LOGIN_FAILED
} from './session.types'
export const login = (email, password) => (
async dispatch => {
console.log('here');
dispatch(loggingIn());
await axios.post(`${API_URL}/login`, {
email,
password
}).then(res => {
dispatch(loginSuccess(res.data.token))
}).catch(err => {
dispatch(loginFailed('Unable to login.'))
})
}
)
export const loggingIn = () => ({
type: LOGGING_IN,
})
export const loginSuccess = (token) => ({
type: LOGIN_SUCCESS,
payload: {
token
}
})
export const loginFailed = (error) => ({
type: LOGIN_FAILED,
payload: {
error
}
})
Since your problem is about Menu not render and Menu is under Main. So, we can ask the question what condition Main component not re-render. Luckily your example Main only depend on solely one props and no state. -I'll say your problem lies on props.token.- Since you initialize your token as null, I'll assume it hold object type. In that case, you need to make sure the token need to be a new object (new reference) else no re-render because react-redux connect by default will check the props changes before trigger the component underneath it.
EDIT: You mentioned the Menu not showing and the token is string, I can think of another reason Main not render is because connect is not trigger. You probably need to check the root of the store and make sure it has the new reference as your code only showing the reducer update state.session but not the state itself.
I'm new to Redux. It's really confusing to understand basic syntax. None of the bugs are found so It's hard to figure out what's wrong with my code.
It worked well last week, I don't remember what I have changed.
//child component
import React, { Component } from 'react';
import { SingleDatePicker } from 'react-dates';
import moment from 'moment';
class InputForms extends Component {
state = {
inputs: ['input-0'],
title: '',
tag: '',
createdAt: moment(),
imageLinks: [''],
calendarFocused: false,
error: '',
}
appendInput(e) {
const newInput = `input-${this.state.inputs.length}`;
this.setState({ inputs: this.state.inputs.concat([newInput]) });
}
onTitleChange = (e) => {
const title = e.target.value;
this.setState(() => ({ title }));
};
onTagChange = (e) => {
const tag = e.target.value;
this.setState(() => ({ tag }));
};
onImageLinkChange = (e) => {
const imageLinks = e.target.value;
this.setState(() => ({ imageLinks: this.state.imageLinks.concat([imageLinks]) }));
};
onDateChange = (createdAt) => {
if (createdAt) {
this.setState(() => ({ createdAt }));
}
};
onFocusChange = ({ focused }) => {
this.setState(() => ({ calendarFocused: focused }));
};
onSubmit = (e) => {
e.preventDefault();
if (!this.state.title || !this.state.imageLinks) {
this.setState(() => ({ error: '제목과 이미지링크를 입력해주세요' }));
} else {
this.setState(() => ({ error: '' }));
this.props.onSubmit({
title: this.state.title,
tag: this.state.tag,
createdAt: this.state.createdAt.valueOf(),
imageLinks: this.state.imageLinks,
});
}
}
render() {
return (
<div>
<form onSubmit={this.onSubmit}>
<input
type="text"
placeholder="제목을 입력하세요"
required
value={this.state.title}
onChange={this.onTitleChange}
/>
<input
type="text"
placeholder="태그를 입력하세요"
value={this.state.tag}
onChange={this.onTagChange}
/>
<SingleDatePicker
date={this.state.createdAt}
onDateChange={this.onDateChange}
focused={this.state.calendarFocused}
onFocusChange={this.onFocusChange}
numberOfMonths={1}
isOutsideRange={() => false}
/>
{this.state.inputs.map((input, key) => {
return <input
key={input}
type="text"
required
value={this.state.imageLinks}
onChange={this.onImageLinkChange}
placeholder={`이미지링크 ${key + 1}`}
/>
})}
<button>저장</button>
</form>
</div>
)
}
}
export default InputForms;
//parent component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import configureStore from '../store/configureStore';
import InputForms from './InputForms';
import { addPost } from '../actions/posts';
const store = configureStore();
class CreatePost extends Component {
onSubmit = (post) => {
this.props.addPost(post);
this.props.history.push('/');
};
render(){
return(
<div>
<InputForms onSubmit={this.onSubmit}/>
</div>
)
}
}
const mapDispatchToProps = (dispatch, props) => ({
addPost: (post) => dispatch(addPost(post))
});
export default connect(undefined, mapDispatchToProps)(CreatePost);
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
import registerServiceWorker from './registerServiceWorker';
import AppRouter from './routers/AppRouter';
import 'normalize.css/normalize.css';
import './style/style.css';
import 'react-dates/lib/css/_datepicker.css';
import 'react-dates/initialize';
const store = configureStore();
const jsx = (
<Provider store={store}>
<AppRouter />
</Provider>
);
ReactDOM.render(jsx, document.getElementById('root'));
registerServiceWorker();
//action
import database from '../firebase/firebase';
//Add Posts
export const addPost = (post) => ({
type: 'ADD_POST',
post
});
//reducer
const postReducerDefaultState = [];
export default (state = postReducerDefaultState, action) => {
switch (action.type) {
case 'ADD_POST':
return [
...state,
action.post
];
default:
return state;
}
};
In your reducer, you return as below
return [ ...state, action.post];
Reducer doesnt return array, but instead returning objects. Secondly, action.post is a value, you need to assign this to key, something like below:
return { ...state, post: action.post };