Я новичок в сокращении (и переполнении стека), и у меня есть некоторые проблемы с моей функцией поиска. Первоначально он отлично работал с данными, которые я установил вручную, но затем я импортировал данные из API, и теперь я не могу заставить поиск работать. Мы будем очень благодарны за любую помощь или совет!
<- actions -> //fetchData.js
import {
FETCH_DATA_REQUEST,
FETCH_DATA_SUCCESS,
FETCH_DATA_FAILURE,
SEARCH_POSTS
} from './types';
export const fetchData = () => {
return (dispatch) => {
dispatch(fetchDataRequest())
axios
.get("https://hn.algolia.com/api/v1/search?query=redux")
.then(response => {
const posts = response.data
dispatch(fetchDataSuccess(posts))
})
.catch(error => {
dispatch(fetchDataFailure(error.message))
})
}
}
export const fetchDataRequest = () => {
return {
type: FETCH_DATA_REQUEST
}
}
const fetchDataSuccess = posts => {
return {
type: FETCH_DATA_SUCCESS,
payload: posts
}
}
const fetchDataFailure = error => {
return {
type: FETCH_DATA_FAILURE,
payload: error
}
}
export function searchData(value) {
return {
type: SEARCH_POSTS,
payload: value
}
}
<--- components ---> // dataContainer . js
import { connect } from 'react-redux';
import { fetchData } from '../actions/fetchData';
import { Card } from 'react-bootstrap';
import { Button } from 'react-bootstrap';
function DataContainer({ results, fetchData }) {
useEffect(() => {
fetchData()
}, [])
return results.loading ? (
<h2>Loading...</h2>
) : results.error ? (
<h2>{results.error}</h2>
) : (
<div>
<h1>Posts</h1>
{results && results.posts && results.posts.map(result =>
<div className ="cardDiv" key={result.objectID}>
<Card>
<Card.Header>By: {result.author}</Card.Header>
<Card.Body>
<Card.Title>{result.title}</Card.Title>
<Card.Text>
{result.body}
</Card.Text>
<Button variant="primary" href={result.url}>See Article</Button>
</Card.Body>
</Card>
{'\n'}
</div>)}
</div>
)
}
const mapStateToProps = state => {
return {
results: state.data
}
}
const mapDispatchToProps = dispatch => {
return {
fetchData: () => dispatch(fetchData())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(DataContainer);
// searchBar. js
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { searchData, fetchData } from '../actions/fetchData';
function SearchBar({ posts, fetchData}) {
useEffect(() => {
fetchData()
}, [])
const { search, value } = posts;
return (
<input
className="form-control mx-auto"
placeholder="Search"
onChange={(e) => searchData(e.target.value)}
value={value}
style={{ maxWidth: '200px', textAlign: 'center' }} />
);
}
function mapStateToProps({ posts, state }) {
return {
posts: state.posts,
value: posts.value
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ searchData, fetchData }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchBar);
// homePage. js
import SearchBar from './searchBar';
import DataContainer from './dataContainer';
import { connect } from 'react-redux';
import * as actions from '../actions/fetchData';
class HomePage extends Component {
handleSearchBarSubmit(query) {
this.props.fetchPostsWithQuery(query, () => {
this.props.history.push('/results');
});
}
render() {
return (
<div className="home">
<SearchBar page="home"/>
<DataContainer/>
</div>
);
}
}
export default connect(null, actions)(HomePage);
<--- Редукторы ---> // dataReducer
FETCH_DATA_REQUEST,
FETCH_DATA_SUCCESS,
FETCH_DATA_FAILURE,
SEARCH_POSTS
} from "../actions/types";
const _ = require('lodash')
const posts = []
const initState = {
loading: false,
posts,
error: '',
filtered: []
}
const reducer = (state = initState, action) => {
switch (action.type) {
case FETCH_DATA_REQUEST:
return {
...state,
loading: true
}
case FETCH_DATA_SUCCESS:
return {
loading: false,
posts: action.payload.hits,
filtered: action.payload.hits,
error: ''
}
case FETCH_DATA_FAILURE:
return {
loading: false,
posts: [],
error: action.payload
}
case SEARCH_POSTS:
const { payload } = action
const filtered = _.filter(state.data.posts, (o) => _.toLower(o.post).includes(_.toLower(payload)))
return {
...state,
filtered
}
default: return state
}
}
export default reducer;
// searchReducer. js
SEARCH_POSTS
} from '../actions/types';
const posts = []
const INIT_STATE = {
posts
}
export default function (state = INIT_STATE, action) {
switch (action.type) {
case SEARCH_POSTS: {
let { value } = action.payload.hits;
const posts = state.posts.filter((post) => post.title.toLowerCase().includes(state.value.toLowerCase()));
return { ...state, value, posts };
}
default:
return state;
}
}
// rootReducer. js
import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form'
import resultsPosts from './searchReducer';
const rootReducer = combineReducers({
data: dataReducer,
resultsPosts,
form
})
export default rootReducer;
<- - Приложение js и индекс. js ---> //app.js
import './App.css';
import HomePage from './components/homePage';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import ResultPage from './components/resultPage'
function App() {
return (
<BrowserRouter>
<div className='App'>
<h1 style={{textAlign: 'center'}}>Search for Hacker News!</h1>
<Switch>
<Route path="/" exact={true} component={HomePage} />
<Route path='/results/:id' component={ResultPage}/>
</Switch>
</div>
</BrowserRouter>
);
}
export default App;
// index. js
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();