Bonjour,

Je rencontre un soucis, dans mon application j'ai 3 niveaux de composants.
  1. Collection
  2. Tableau
  3. Bouton


Lorsque je clique sur mon bouton j'ai bien ma saga qui est appelé et l'appel ajax s'effectue correctement. Par contre je dois appuyé deux fois sur mon bouton pour que la ligne et le bouton en question se mette à jour sur l'affichage.
Quand je regarde l'évolution de mon state je vois bien le changement de donnée.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
 
class MyCollection extends React.Component {
 
  constructor(props) {
    super(props);
 
    this.handleSubmit = this.handleSubmit.bind(this);
  }
 
  componentDidMount() {
    this.props.filterSearchList([]);
  }
 
  handleSubmit(event) {
    event.preventDefault();
    const data = new FormData(event.target);
    this.props.filterSearchList(data);
  }
 
  addBook(book) {
    this.props.bookAdd(book.id);
  }
 
  render() {
    return (
      <div>
        <ContainerHeader match={this.props.match} title={<IntlMessages id="pages.myCollection" />} />
        <div className="row">
          <div className="col-12">
            <form onSubmit={this.handleSubmit} className="jr-card pt-0">
              <FormControl className="w-100 mb-2">
                <TextField
                  id="searchText"
                  label={<IntlMessages id="common.search" />}
                  name="searchText"
                />
              </FormControl>
            </form>
          </div>
        </div>
        <BookListTable list={this.props.list} addBookCallBack={this.addBook.bind(this)} />
        {/* <BookListCard list={this.props.list} /> */}
      </div>
    );
  }
}
 
const mapStateToProps = state => ({
  list: state.list,
  book: state.book,
});
 
const mapDispatchToProps = dispatch => (
  bindActionCreators({
    filterSearchList,
    bookAdd
  }, dispatch)
);
 
export default connect(mapStateToProps, mapDispatchToProps)(MyCollection);
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 
class BookListTable extends Component {
  constructor(props) {
    super(props);
 
    this.state = {
      page: 0,
      rowsPerPage: 5,
    };
  }
 
  handleChangePage = (event, page) => {
    this.setState({page});
  };
 
  handleChangeRowsPerPage = (event) => {
    this.setState({rowsPerPage: event.target.value});
  };
 
  addBook(book) {
    this.props.addBookCallBack(book);
  }
 
  render() {
    const {classes, list} = this.props;
    const {rowsPerPage, page} = this.state;
    const emptyRows = rowsPerPage - Math.min(rowsPerPage, list.length - (page * rowsPerPage));
    return (
      <div className="row">
        <CardBox styleName="col-lg-12" cardStyle="p-0">
          <div className={classes.tableWrapper}>
            <Table className={classes.table}>
              <TableBody>
                {list.slice(page * rowsPerPage, (page * rowsPerPage) + rowsPerPage).map(n => (
                  <TableRow key={n.id}>
                    <TableCell>
                      <div className="user-profile d-flex flex-row align-items-center">
                        <Avatar
                          alt={n.title}
                          src={n.image}
                          className="ml-3 user-avatar"
                        />
                        <div className="user-detail">
                          <h5 className="user-name">{n.title} - {n.author} - {n.isbn}</h5>
                          <p className="user-description">{n.tags}</p>
                        </div>
                      </div>
                    </TableCell>
                    <TableCell>{n.addDate}</TableCell>
                    <TableCell className={classes.tableCell}>
                      <AddRemoveBook className="mr-2" bookId={n.id} bookInfos={n} addBookCallBack={this.addBook.bind(this)} />
                      <UpdateBook className="mr-2" />
                      <ReportBook className="mr-2" />
                    </TableCell>
                  </TableRow>
                ))}
                {emptyRows > 0 && (
                  <TableRow style={{height: 48 * emptyRows}}>
                    <TableCell colSpan={6} />
                  </TableRow>
                )}
              </TableBody>
              <TableFooter>
                <TableRow>
                  <TablePagination
                    colSpan={3}
                    count={list.length}
                    rowsPerPage={rowsPerPage}
                    page={page}
                    onChangePage={this.handleChangePage}
                    onChangeRowsPerPage={this.handleChangeRowsPerPage}
                    ActionsComponent={TablePaginationActionsWrapped}
                    labelRowsPerPage={<IntlMessages id="common.bookPerPage" />}
                    labelDisplayedRows={({ from, to, count }) => <IntlMessages id="common.labelDisplayedRows" values={{ from, to, count }} />}
                  />
                </TableRow>
              </TableFooter>
            </Table>
          </div>
        </CardBox>
      </div>
    );
  }
}
 
BookListTable.propTypes = {
  classes: PropTypes.object.isRequired,
};
 
export default withStyles(styles)(BookListTable);
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 
class AddRemoveBook extends React.PureComponent {
 
  constructor(props) {
    super(props);
    this.state = {
      bookInfos: props.bookInfos,
      open: false,
    };
 
    this.handleClick = this.handleClick.bind(this);
  }
 
  handleClickOpen = () => {
    this.setState({open: true});
  };
 
  handleRequestClose = () => {
    this.setState({open: false});
  };
 
  handleClick(event) {
    event.preventDefault();
    this.props.addBookCallBack(this.state.bookInfos);
  }
 
  renderAdd(className) {
    return (
      <Tooltip title={<IntlMessages id="common.addToCollection" />} >
        <Button
          variant="fab"
          className={`jr-fab-btn jr-btn-fab-xs bg-green text-white ${className}`}
          onClick={this.handleClick}
        >
          <i className="zmdi zmdi-plus zmdi-hc-fw" />
        </Button>
      </Tooltip>
    );
  }
 
  renderRemove(className) {
    return (
      <span>
        <Tooltip title={<IntlMessages id="common.removeFromCollection" />} >
          <Button
            variant="fab"
            className={`jr-fab-btn jr-btn-fab-xs bg-grey text-white ${className}`}
            onClick={this.handleClickOpen}
          >
            <i className="zmdi zmdi-delete zmdi-hc-fw" />
          </Button>
        </Tooltip>
        <Dialog open={this.state.open} onClose={this.handleRequestClose}>
          <DialogContent>
            <DialogContentText>
              <IntlMessages id="common.removeAlertQuestion" />
            </DialogContentText>
          </DialogContent>
          <DialogActions>
            <Button
              onClick={this.handleRequestClose}
              className="jr-btn jr-btn bg-red text-white"
            >
              <IntlMessages id="common.cancel" />
            </Button>
            <Button
              onClick={this.handleRequestClose}
              className="jr-btn jr-btn bg-green text-white"
            >
              <IntlMessages id="common.confirm" />
            </Button>
          </DialogActions>
        </Dialog>
      </span>
    );
  }
 
  render() {
    const {className, bookInfos} = this.props;
    return (
      <span>
        {bookInfos.inCollection && this.renderAdd(className)}
        {!bookInfos.inCollection && this.renderRemove(className)}
      </span>
    );
  }
}
 
 
export default AddRemoveBook;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 
import {
  LIST_SEARCH_FILTER,
  LIST_SEARCH_FILTER_SUCCESS,
  LIST_UPDATE_IN_COLLECTION
} from 'constants/ActionTypes';
 
 
export default (state = [], action) => {
  switch (action.type) {
    case LIST_SEARCH_FILTER: {
      return state;
    }
    case LIST_SEARCH_FILTER_SUCCESS: {
      return action.payload;
    }
    case LIST_UPDATE_IN_COLLECTION: {
      const index = state.findIndex(book => book.id === action.payload.id);
      state[index] = action.payload;
      return state;
    }
    default:
      return state;
  }
};
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
import {
  BOOK_ADD_SUCCESS
} from 'constants/ActionTypes';
 
 
export default (state = [], action) => {
  switch (action.type) {
    case BOOK_ADD_SUCCESS: {
      return action.payload;
    }
    default:
      return state;
  }
};
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
 
import axios from 'axios';
import {all, call, fork, put, takeEvery} from 'redux-saga/effects';
import {
  BOOK_ADD,
} from 'constants/ActionTypes';
 
import {bookAddSuccess} from 'actions/Book';
import {updateBookInList} from 'actions/List';
 
let counter = 0;
 
function createData(title, author, image, addDate, tags, isbn, inCollection) {
  counter += 1;
  return {id: counter, title, author, image, addDate, tags, isbn, inCollection};
}
 
function* add(action) {
  try {
    const result = yield call(action => axios.get(
      'https://my-json-server.typicode.com/typicode/demo/posts'
    )
      .then((res) => {
        counter = 0;
        return createData('Sans Application', 'Renewal', 'http://via.placeholder.com/150x150', '6:06', '$54.20', 'ISBN', false);
      })
      .catch((err) => { throw err; })
      , action);
 
    yield put(bookAddSuccess(result));
    yield put(updateBookInList(result));
  } catch (error) {
    console.log(error);
  }
}
 
export function* addToCollection() {
  yield takeEvery(BOOK_ADD, add);
}
 
export default function* rootSaga() {
  yield all(
    [
      fork(addToCollection)
    ]
  );
}
De ce que je comprends le soucis vient du fait que le changement de state se fait de manière asynchrone mais je vois pas comment procéder pour lui dire de se mettre à jour au moment ou le state est réellement à jour.